다이나믹 테이블 리프레시 모드

다이나믹 테이블 리프레시 모드

모든 다이나믹 테이블은 내용물을 어떻게 리프레시할지 제어하는 리프레시 모드를 가집니다. Snowflake가 관리하는 모드는 ADAPTIVE, FULL, INCREMENTAL, AUTO 네 가지예요. 고급 사용 사례에서는 CUSTOM_INCREMENTAL로 자신만의 리프레시 로직을 정의할 수 있습니다. 리프레시 모드는 생성 시점에 설정되며, 변경하려면 CREATE OR REPLACE로 다시 만들거나 CREATE OR ALTER DYNAMIC TABLE을 사용해야 해요. 어떤 전환이 재초기화를 유발하는지에 대한 자세한 내용은 Refresh mode transitions를 참고하세요.

-- CREATE 문에서 리프레시 모드를 명시적으로 설정
CREATE OR REPLACE DYNAMIC TABLE dt_orders
  TARGET_LAG = '10 minutes'
  WAREHOUSE = transform_wh
  REFRESH_MODE = ADAPTIVE
  -- or FULL, AUTO, or INCREMENTAL
AS
  SELECT ... FROM raw_orders ...;

모든 옵션이 포함된 전체 CREATE 문법은 CREATE DYNAMIC TABLE을 참고하세요.

출처: Snowflake 문서

본문

ADAPTIVE 리프레시

ADAPTIVE 리프레시는 기본적으로 증가분 리프레시를 사용하지만, 내부 휴리스틱이 증가분 리프레시가 처음부터 다시 구축하는 것보다 훨씬 더 비쌀 것이라고 감지하면 다이나믹 테이블을 자동으로 재초기화합니다. 재초기화가 완료되면 다이나믹 테이블은 증가분 리프레시를 재개해요.

팁: 모든 증가분 워크로드의 기본 리프레시 모드로 ADAPTIVE를 사용하세요.

재초기화가 트리거되는 경우

ADAPTIVE 리프레시는 Snowflake가 증가분 리프레시가 재초기화보다 더 비싸다고 판단할 때 재초기화를 트리거합니다. 재초기화를 유발하는 일반적인 시나리오는 다음과 같습니다:

  • base table에 대한 INSERT OVERWRITE (파티션 또는 전체 테이블의 모든 행을 교체)
  • 다이나믹 테이블이 추적하는 행 중 큰 비율에 영향을 주는 대량 삭제 또는 갱신

일반적으로 재초기화가 트리거되지 않는 경우

ADAPTIVE 리프레시는 다음 경우에는 보통 재초기화하지 않습니다:

  • 정의의 비싼 연산자. 정의가 Cortex AI 함수, 사용자 정의 함수(UDF), 외부 함수를 사용하면, 모든 행에 걸쳐 그 연산자를 다시 실행하는 비용이 새로 구축하는 이득보다 크기 때문에 휴리스틱이 재초기화를 건너뛰는 경향이 있어요.
  • 단순한 정의. 프로젝션, 필터, 정렬만 포함하는 정의는 증가분 리프레시가 이 패턴에 효율적이므로 보통 재초기화를 유발하지 않습니다.

관측 가능성

ADAPTIVE 다이나믹 테이블이 재초기화하는지 확인하려면 DYNAMIC_TABLE_REFRESH_HISTORY 함수를 조회하세요. 재초기화는 REFRESH_ACTION = 'REINITIALIZE'인 행으로 나타납니다. 리프레시가 REFRESH_ACTION = 'REINITIALIZE'를 사용한 이유를 알려면 DYNAMIC_TABLE_REFRESH_HISTORY의 REINIT_REASON 컬럼을 확인하세요.

팁: 재초기화의 영향을 줄이려면 다이나믹 테이블에 frozen region을 정의하는 것을 고려하세요. Frozen regions 참고.

사용 사례

증분화(incrementalizable)할 수 있는 워크로드에는 ADAPTIVE 리프레시를 사용하세요. 정의는 증분 리프레시가 지원하는 구문만 사용해야 합니다 (INCREMENTAL 모드와 동일한 요구사항). ADAPTIVE는 증가분 처리를 더욱 개선하며, 특히 base table이 가끔 대량 로드(INSERT OVERWRITE)나 대규모 갱신을 받지만 대부분의 리프레시는 작은 변경량을 처리할 때 효과적이에요.

초기화 웨어하우스

INITIALIZATION_WAREHOUSE를 구성하면 ADAPTIVE 리프레시는 재초기화에 그 웨어하우스를 사용합니다. 이렇게 하면 가끔 발생하는 전체 재초기화에는 더 큰 웨어하우스를 할당하면서, 일상적인 증가분 리프레시에 사용하는 웨어하우스는 과잉 프로비저닝하지 않을 수 있어요.

제약 사항

  • ADAPTIVE는 INCREMENTAL과 동일한 증분화 가능 쿼리 구문을 요구합니다. 정의에 증가분 리프레시를 지원하지 않는 구문이 있으면 SQL 컴파일 에러로 생성이 실패해요.
  • ADAPTIVE는 INCREMENTAL과 동일한 파이프라인 제약이 있습니다: 상위 FULL 리프레시 다이나믹 테이블이 시스템 파생 고유 키나 frozen region을 갖지 않는 한, 그 아래에만 올 수 있어요.
  • ADAPTIVE 다이나믹 테이블은 다른 ADAPTIVE 또는 INCREMENTAL 다이나믹 테이블의 상위나 하위가 될 수 있습니다.

예시

CREATE OR REPLACE DYNAMIC TABLE dt_orders_adaptive
  TARGET_LAG = '10 minutes'
  WAREHOUSE = transform_wh
  INITIALIZATION_WAREHOUSE = transform_wh_xl
  REFRESH_MODE = ADAPTIVE
AS
  SELECT order_id, customer_id, order_date, total_amount
  FROM raw_orders
  WHERE order_status = 'COMPLETED';

이 예시에서 raw_orders는 하루 종일 추가 행을 정상적으로 받습니다(증분 처리). 주기적으로 상위 ETL 프로세스가 INSERT OVERWRITE로 테이블을 다시 로드해요. 그렇게 되면 ADAPTIVE 리프레시가 큰 변경을 감지하고 large_init_wh를 사용해 재초기화한 다음, 다음 주기에 증가분 리프레시를 재개합니다.

INCREMENTAL 리프레시

증가분 리프레시는 마지막 리프레시 이후 변경된 데이터만 분석해 다이나믹 테이블에 병합합니다. 변경되지 않은 데이터를 다시 처리하지 않으므로 일반적으로 가장 효율적인 모드예요.

증가분 리프레시는 다음 경우에 사용하세요:

  • 정의가 증분 리프레시가 지원하는 구문만 사용할 때
  • 리프레시 사이에 base table 데이터의 5% 미만이 변경될 때
  • 리프레시당 웨어하우스 컴퓨팅 비용을 최소화하고 싶을 때

정의에 증가분 리프레시를 지원하지 않는 구문이 있으면, 어떤 구문이 호환되지 않는지 식별하는 SQL 컴파일 에러로 생성이 실패합니다.

FULL 리프레시

전체 리프레시는 모든 base table의 현재 상태에 대해 전체 정의를 다시 실행하고, 구체화된 출력을 새 쿼리 결과로 대체합니다.

전체 리프레시는 다음 경우에 사용하세요:

  • 정의가 증가분 리프레시가 지원하지 않는 구문을 사용할 때 (예: INTERSECT, EXCEPT, 또는 PERCENTILE_CONT 같은 정확한 백분위 함수)
  • 리프레시 사이에 base table 데이터의 큰 비율이 변경되어 증가분 처리가 전체 스캔보다 더 비쌀 때
  • 리프레시 사이에 얼마나 많은 데이터가 변경되든 예측 가능한 성능이 필요할 때
CREATE OR REPLACE DYNAMIC TABLE dt_non_null_orders
  TARGET_LAG = '1 hour'
  WAREHOUSE = transform_wh
  REFRESH_MODE = FULL
AS
  SELECT order_id, product_name, quantity, unit_price
  FROM raw_orders
  EXCEPT
  SELECT order_id, product_name, quantity, unit_price
  FROM raw_orders
  WHERE order_status IS NULL;

EXCEPT는 증가분 리프레시가 지원하지 않으므로 이 정의는 REFRESH_MODE = FULL이 필요합니다.

일부 계산이 전체 리프레시를 요구하는 이유

증가분 리프레시는 마지막 리프레시 이후 변경된 행만 처리해 동작합니다. 각 새 행의 출력을 그 자체로 계산할 수 있을 때 효율적이에요 (예: 행 필터링, 형식 함수 적용, 새 주문을 고객 테이블에 조인). 새 행은 이전 행에 대해 이미 계산된 결과에 영향을 주지 않습니다.

하지만 일부 계산은 새 행뿐 아니라 모든 행 간의 관계에 의존합니다. 주문 값의 중앙값을 찾는 경우를 생각해보세요. 1,000개의 주문이 있고 새 주문 5개가 도착하면 중앙값이 바뀔 수 있어요. 새 중앙값을 찾으려면 1,005개 값을 모두 다시 정렬해 중간 값을 골라야 합니다. 5개 새 행만 보고 답을 갱신하는 지름길은 없습니다.

이런 계산은 리프레시마다 모든 데이터를 봐야 합니다:

계산 모든 행이 필요한 이유 예시
정확한 백분위/중앙값 행 하나 추가로 정렬 목록 중간에 오는 값이 바뀔 수 있음 PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount)
집합 차/교집합 한 집합에 있고 다른 집합에 없는 것을 판별하려면 두 전체 집합을 비교해야 함 SELECT ... EXCEPT SELECT ...

이런 계산은 모든 행에 접근해야 하므로 전체 리프레시가 필요합니다. DDL을 읽는 사람이 비용 트레이드오프를 이해하도록 REFRESH_MODE = FULL을 명시적으로 설정하세요.

전체 리프레시가 올바른 출발점인 일반 워크로드

증가분 리프레시는 리프레시 사이에 데이터의 작은 비율이 변경되는 워크로드용으로 설계되었습니다. 데이터 변경 패턴이 이 모델에 맞지 않으면 전체 리프레시가 적절한 시작 모드입니다.

  • 대량 재로드 또는 truncate-and-replace 패턴. 상위 시스템이 모든 로드 주기마다 모든 행을 교체하면, 증가분 리프레시는 전체 리프레시보다 더 많은 작업을 합니다. REFRESH_MODE = FULL을 설정하세요. INSERT OVERWRITE가 실제 데이터 값을 바꾸지 않고 RELY로 기본 키를 정의할 수 있다면 증가분 리프레시도 여전히 효율적일 수 있어요. 기본 키로 INSERT OVERWRITE 워크로드를 최적화하는 방법은 Optimize input data for dynamic tables 참고.
  • 전체를 다시 만드는 차원 테이블. 작은~중간 크기 차원 테이블은 전체적으로 주기적으로 교체되는데, diff보다 다시 구축하는 게 빠를 때가 많아요.
  • 여러 blocking 연산자를 쌓는 정의. 쿼리가 여러 blocking 연산자(GROUP BY, DISTINCT, 윈도우 함수)를 결합하면 전체 리프레시가 증가분보다 나을 수 있습니다. 복잡한 쿼리를 나누는 안내는 Optimize queries for incremental refresh 참고.
  • 삭제가 많은 높은 변동(churn) 워크로드. base table이 대량의 만료 데이터를 주기적으로 삭제하면, 증가분 리프레시는 삭제된 행을 식별하기 위해 많은 마이크로파티션 파일을 스캔해야 합니다. 전체 리프레시는 이 오버헤드를 완전히 피해요.

증가분 리프레시가 기존 다이나믹 테이블에 도움이 되는지 해가 되는지 판단하려면 DYNAMIC_TABLE_REFRESH_HISTORY로 평균 리프레시 시간을 비교하세요. 자세한 내용은 Optimize queries for incremental refresh 참고.

AUTO 리프레시

AUTO는 기본 리프레시 모드입니다. REFRESH_MODE = AUTO를 설정하면(또는 파라미터를 생략하면) Snowflake가 생성 시점에 정의를 한 번 평가해 INCREMENTAL 또는 FULL로 결정합니다. 생성 후 결정된 모드는 고정되어 바뀌지 않아요. 모든 옵션이 포함된 전체 CREATE 문법은 CREATE DYNAMIC TABLE을 참고하세요.

중요: AUTO는 생성 시점에 한 번만 결정됩니다. 이후 리프레시에서 재평가하지 않아요. 나중에 무언가 바뀌면(예: 상위 뷰가 수정되어 증가분 리프레시를 더 이상 지원하지 않게 됨) 경고 없이 전체 리프레시로 전환하는 대신 리프레시가 실패합니다. 리프레시 모드를 바꾸려면 CREATE OR REPLACE DYNAMIC TABLE로 다시 만드세요.

AUTO가 선택하는 방식

Snowflake는 정의의 구문, 연산자, 함수, base table 속성을 검사합니다. 정의가 증가분 리프레시를 지원하고 Snowflake가 그것이 비용·시간 면에서 더 효과적일 것으로 기대하면, AUTO는 INCREMENTAL로 결정합니다. 정의의 어떤 부분이든 호환되지 않거나, 정의 복잡성 때문에 성능을 예측하기 어려우면 Snowflake는 FULL로 결정해요. AUTO는 ADAPTIVE로 결정하지 않습니다. ADAPTIVE 리프레시를 쓰려면 REFRESH_MODE = ADAPTIVE를 명시적으로 설정하세요.

AUTO가 FULL로 결정하면 CREATE 문은 성공하고 결과 메시지가 FULL이 선택된 이유를 나타냅니다. 생성 후 SHOW DYNAMIC TABLES로 결정된 모드를 확인하세요.

결정된 모드 확인

SHOW DYNAMIC TABLES를 실행하고 refresh_mode와 refresh_mode_reason 컬럼을 확인하세요:

SHOW DYNAMIC TABLES LIKE 'dt_orders'
-> SELECT "name", "refresh_mode", "refresh_mode_reason" FROM $1;
+-------------------+--------------+---------------------+
| name              | refresh_mode | refresh_mode_reason |
|-------------------+--------------+---------------------|
| DT_ORDERS         | INCREMENTAL  | NULL                |
+-------------------+--------------+---------------------+

refresh_mode가 INCREMENTAL이면 refresh_mode_reason은 NULL입니다. AUTO가 FULL로 결정하면 refresh_mode_reason 컬럼이 증가분 리프레시가 불가능했던 이유를 설명합니다:

+------------------------+--------------+-------------------------------------------------------+
| name                   | refresh_mode | refresh_mode_reason                                   |
|------------------------+--------------+-------------------------------------------------------|
| DT_NON_NULL_ORDERS     | FULL         | Unsupported query operator type: SetOperation[EXCEPT] |
+------------------------+--------------+-------------------------------------------------------+

팁: 프로덕션에서는 AUTO에 의존하지 말고 리프레시 모드를 ADAPTIVE, FULL, INCREMENTAL 중 하나로 명시적으로 설정하세요. 명시적 모드는 비용을 예측 가능하게 하고 전체 리프레시로의 조용한 폴백을 방지합니다. 탐색과 프로토타이핑에는 AUTO를 사용하고, 프로덕션으로 승격하기 전에 결정된 모드를 확정하세요.

커스텀 증분화

표준 리프레시 모드로 변환을 표현할 수 없을 때, 커스텀 증분화를 사용하면 REFRESH USING 절 안에서 MERGE 또는 INSERT DML로 리프레시 로직을 정의할 수 있어요. 커스텀 증가분 다이나믹 테이블은 표준 쿼리 분석을 완전히 우회합니다. 변경을 테이블에 적용하는 방식을 완전히 제어하게 됩니다. 문법과 예시는 Custom incrementalization을 참고하세요.

리프레시 모드 선택

이 표로 다이나믹 테이블에 설정할 리프레시 모드를 결정하세요:

기준 ADAPTIVE FULL INCREMENTAL AUTO
가장 적합한 경우 증분화할 수 있는 모든 워크로드. 가끔 대량 로드가 있는 워크로드에 탁월 지원되지 않는 구문이나 높은 변경량이 있는 정의 소량 변경이 있는 append-heavy base table 탐색과 프로토타이핑
리프레시당 비용 보통 더 낮음, 대량 변경 시 가끔 재초기화 더 높음 (모든 데이터 재처리) 더 낮음 (변경된 데이터만 처리) 결정된 모드에 따름
지원되지 않는 구문 생성이 즉시 실패 (INCREMENTAL과 동일) 항상 동작 생성이 즉시 실패 조용히 FULL로 결정
모드 가시성 DDL에 명시적 DDL에 명시적 DDL에 명시적 SHOW DYNAMIC TABLES로 확인 필요
프로덕션 권장 예, 소스가 append와 bulk-load 패턴이 혼합될 때 예, 증가분이 불가능하거나 더 느릴 때 예, 정의가 호환될 때 탐색에 사용 후 명시적으로 설정

리프레시 모드는 ALTER로 변경할 수 없음

ALTER DYNAMIC TABLE은 리프레시 모드 변경을 지원하지 않습니다. 모드를 바꾸려면 CREATE OR ALTER DYNAMIC TABLE이나 CREATE OR REPLACE DYNAMIC TABLE을 사용하세요. 전환 세부사항은 Refresh mode transitions을 참고하세요.

참고: CREATE OR REPLACE는 다이나믹 테이블을 drop하고 다시 만들어, 테이블과 하위 의존성에 대해 재초기화를 유발합니다.

증가분 리프레시에는 고정 폴백 임계값이 없음

refresh optimization 페이지는 최상의 증가분 성능을 위해 리프레시 사이에 데이터의 5% 미만 변경을 유지할 것을 권장합니다. 5% 수치는 성능 가이드라인입니다. Snowflake는 변경량에 따라 다이나믹 테이블의 리프레시 모드를 자동으로 전환하지 않아요.

실제 일어나는 일: base table 데이터의 큰 비율이 리프레시 사이에 변경되면 증가분 리프레시가 전체 리프레시보다 느려질 수 있습니다. 구성된 리프레시 모드는 설정한 대로 유지됩니다. Snowflake는 다른 모드로 자동 전환하지 않아요.

워크로드에서 증가분 리프레시가 전체 리프레시보다 일관되게 느리다면 REFRESH_MODE = FULL로 다이나믹 테이블을 다시 만드세요. 또는 느려지는 원인이 일관된 높은 변경률이 아니라 가끔 있는 대량 로드 때문이라면 REFRESH_MODE = ADAPTIVE를 고려하세요. ADAPTIVE는 큰 변경이 감지되면 자동으로 재초기화하고 이후 증가분 리프레시를 재개합니다.

파이프라인의 리프레시 모드

파이프라인의 각 다이나믹 테이블은 고유한 리프레시 모드를 가질 수 있어요. 전체 리프레시를 사용하는 상위 다이나믹 테이블이 하위 다이나믹 테이블까지 전체 리프레시를 강제하지는 않습니다.

한 가지 제약이 적용됩니다: INCREMENTAL 또는 ADAPTIVE 리프레시 모드를 사용하는 다이나믹 테이블은, 상위 전체 리프레시 다이나믹 테이블이 시스템 파생 고유 키나 frozen region을 갖고 있을 때만 그 아래에 올 수 있어요. 자세한 내용은 Optimize input data for dynamic tables과 Frozen regions and backfill을 참고하세요.

더 알아보기 (Learn more)