Rollup Rebuild 사용자 가이드
개요
이상 데이터가 수집되면 원본 데이터는 삭제 후 정상 데이터로 다시 넣을 수 있지만, 이미 생성된 rollup 통계는 자동으로 되감기지 않습니다. 이 경우 영향 버킷의 rollup 데이터를 다시 만들어야 합니다.
Machbase 환경에서는 서버 내장 Procedure EXEC ROLLUP_REBUILD(...)로 rollup을 재구성합니다.
- built-in rollup + rollup extension + custom rollup 대상
- SQL에서 직접 호출 가능
- custom rollup dependency tree를 따라 stop/rebuild/start 수행
제약사항
EXEC ROLLUP_REBUILD(...)에는 아래 제약이 있습니다.
- standard edition에서 지원하며, cluster edition에서는 지원하지 않습니다.
table_name,tag_name,begin_time,end_time기준의 단일 tag rebuild만 지원합니다.- rebuild 대상 구간은 부분 시간이 아니라 영향 버킷 전체를 기준으로 delete 후 insert 해야 합니다.
Rollup Rebuild 프로시저 사용법
호출 문법
EXEC ROLLUP_REBUILD(table_name, tag_name, begin_time, end_time);예:
EXEC ROLLUP_REBUILD(tag,
'tag-00045',
TO_DATE('2025-09-02 01:00:00'),
TO_DATE('2025-09-02 01:00:00'));
EXEC ROLLUP_REBUILD(sys.tag,
'tag-00045',
TO_DATE('2025-09-02 01:00:00'),
TO_DATE('2025-09-02 01:00:00'));매개변수 의미
table_name- 대상 source TAG 테이블 이름
- 필요 시
schema.table형태 사용
tag_name- 재구성 대상 tag key 값
begin_time- 이상 데이터 보정 시작 시각
end_time- 이상 데이터 보정 종료 시각
적용 범위
- built-in rollup
- rollup extension
- custom rollup
- rollup-on-rollup dependency 환경
Custom Rollup Rebuild 원리
왜 built-in처럼 고정 SQL로 처리할 수 없는가
custom rollup은 아래가 모두 사용자 정의입니다.
- destination table 이름
- destination 컬럼 수와 타입
- 집계 함수
- source가 root table인지, 다른 rollup destination인지
따라서 범용 rebuild는 “고정 스키마 재삽입"이 아니라 “원래 custom SELECT를 다시 실행하되, tag/time 버킷만 좁혀서 다시 넣는 방식"이어야 합니다.
버킷 경계 확장
예를 들어 1분 custom rollup에서 원본 이상 구간이 아래와 같다고 가정합니다.
- 원본 오류 시간:
2026-01-27 09:30:12~2026-01-27 09:31:07
실제 rebuild 대상은 아래처럼 버킷 전체입니다.
- 시작 버킷:
2026-01-27 09:30:00 - 종료 버킷:
2026-01-27 09:31:59.999999999
부분 집계 row가 destination table에 이미 있을 수 있으므로, 삭제 없이 다시 insert만 하면 중복 집계가 발생합니다. 항상 대상 버킷을 먼저 삭제한 뒤 다시 insert 해야 합니다.
수동 rebuild 절차
프로시저를 사용하지 않고 수동으로 하려면 아래 절차를 따라야 합니다.
- 영향 받는 모든 custom rollup stop
- source 이상 데이터 수정 또는 재적재
- 버킷 경계 계산
- destination delete
- 원래
CREATE ROLLUP ... AS (SELECT ...)와 같은 집계식으로 재insert - destination flush
- 상위 custom rollup이 있으면 하위부터 차례대로 반복
- rollup start
Custom Rollup 수동 Rebuild 예제
1분 custom rollup
아래는 stock_tick -> stock_rollup_1m에 대해 09:30 ~ 09:31 버킷을 다시 만드는 예제입니다.
STOP ROLLUP rollup_stock_1m;
DELETE FROM stock_rollup_1m
WHERE time BETWEEN TO_DATE('2026-01-27 09:30:00')
AND TO_DATE('2026-01-27 09:31:59');
INSERT INTO stock_rollup_1m
SELECT code,
DATE_TRUNC('minute', time) AS time,
SUM(price) AS sum_price,
SUM(volume) AS sum_volume,
COUNT(*) AS cnt
FROM stock_tick
WHERE time BETWEEN TO_DATE('2026-01-27 09:30:00')
AND TO_DATE('2026-01-27 09:31:59')
GROUP BY code, time;
EXEC TABLE_FLUSH('stock_rollup_1m');
START ROLLUP rollup_stock_1m;FIRST/LAST 가 있는 custom rollup
FIRST/LAST를 쓰는 custom rollup은 보조 시간 컬럼도 같이 다시 계산해야 합니다.
STOP ROLLUP rollup_stock_candle_1m;
DELETE FROM stock_candle_1m
WHERE time = TO_DATE('2026-01-27 09:30:00');
INSERT INTO stock_candle_1m
SELECT code,
DATE_TRUNC('minute', time) AS time,
MIN(time) AS firsttime,
MAX(time) AS lasttime,
FIRST(time, price) AS open,
MAX(price) AS high,
MIN(price) AS low,
LAST(time, price) AS close,
SUM(volume) AS volume,
COUNT(*) AS cnt
FROM stock_tick
WHERE time BETWEEN TO_DATE('2026-01-27 09:30:00')
AND TO_DATE('2026-01-27 09:30:59')
GROUP BY code, time;
EXEC TABLE_FLUSH('stock_candle_1m');
START ROLLUP rollup_stock_candle_1m;최종 조회는 기존과 동일하게 FIRST(firsttime, open), LAST(lasttime, close)로 재병합해야 합니다.
rollup-on-rollup 순서
예:
- 1차:
stock_tick -> stock_rollup_1m - 2차:
stock_rollup_1m -> stock_rollup_1h
rebuild 순서는 반드시 하위부터입니다.
stock_rollup_1hstopstock_rollup_1mstopstock_rollup_1mrebuildstock_rollup_1hdelete / rebuildstock_rollup_1mstartstock_rollup_1hstart
상위를 먼저 재구성하면 아직 복원되지 않은 하위 결과를 읽게 되어 다시 잘못된 집계가 들어갑니다.
운영 권장사항
- custom rollup rebuild 전에는 먼저 영향 버킷 범위를 확인합니다.
- 운영 적용 전후에
v$rollup으로 dependency를 확인합니다. - custom rollup destination table은 append-only 결과가 누적되므로 rebuild 시 반드시 delete 후 insert 합니다.
- rollup-on-rollup 구조에서는 반드시 하위부터 rebuild 하고, 이후 상위를 재구성합니다.
- 오류 구간이 여러 버킷에 걸치면 전체 범위를 지정해서
EXEC ROLLUP_REBUILD(...)를 호출합니다.
최신 데이터를 포함한 효율적인 Rollup 질의 정리
앞서 설명한 최신 데이터 조회 원칙은 built-in rollup과 custom rollup 모두 동일합니다.
핵심 규칙:
- 안정 구간은 rollup table 사용
- 최근 구간은 source table 직접 집계
- 두 결과를
UNION ALL - 필요하면 바깥에서 한 번 더 최종 집계
일반 1분 rollup 예
SELECT ROLLUP('minute', 1, time) AS mtime, AVG(value)
FROM tag
WHERE name = 'TAG_0001'
AND time < DATE_TRUNC('minute', SYSDATE) - 2m
GROUP BY mtime
UNION ALL
SELECT DATE_TRUNC('minute', time) AS mtime, AVG(value)
FROM tag
WHERE name = 'TAG_0001'
AND time >= DATE_TRUNC('minute', SYSDATE) - 2m
GROUP BY mtime;일반 20분 집계 예
SELECT ROLLUP('minute', 20, time) AS mtime, AVG(value)
FROM tag
WHERE name = 'TAG_0001'
AND time < DATE_BIN('minute', 20, SYSDATE, 0) - 20m
GROUP BY mtime
UNION ALL
SELECT DATE_BIN('minute', 20, time, 0) AS mtime, AVG(value)
FROM tag
WHERE name = 'TAG_0001'
AND time >= DATE_BIN('minute', 20, SYSDATE, 0) - 20m
GROUP BY mtime;custom rollup 예
custom rollup도 동일합니다. 단, rollup 쪽은 destination table을 읽고, 최근 구간은 source를 직접 다시 집계합니다.
SELECT code, time,
SUM(sum_price) / SUM(cnt) AS avg_price
FROM (
SELECT code, time,
SUM(sum_price) AS sum_price,
SUM(cnt) AS cnt
FROM stock_rollup_1m
WHERE time < DATE_TRUNC('minute', SYSDATE) - 2m
GROUP BY code, time
UNION ALL
SELECT code,
DATE_TRUNC('minute', time) AS time,
SUM(price) AS sum_price,
COUNT(*) AS cnt
FROM stock_tick
WHERE time >= DATE_TRUNC('minute', SYSDATE) - 2m
GROUP BY code, time
)
GROUP BY code, time
ORDER BY code, time;