주요 컨텐츠로 이동
제품

AUTO CDC의 진화: 가장 까다로운 실제 사용 사례 해결

이중 시간(bitemporal) 준수부터 부분 레코드 업데이트까지: 커스텀 코드 없이 강력하고 감사 준비가 된 변경 데이터 캡처 제공

작성자: Josh Seidel, Shanelle Roman , Sudhanva Huruli

  • AUTO CDC는 변경 데이터 캡처를 위해 직접 작성해야 했던 MERGE 로직을 선언형 파이프라인으로 대체합니다.
  • Spark 선언형 파이프라인은 이제 누락된 필드를 안전하게 처리하는 부분 업데이트(Partial Updates)와 함께, 비즈니스 시간과 시스템 시간을 독립적으로 추적하는 이중 시간(Bitemporal) AUTO CDC를 지원합니다.
  • AUTO CDC 기능이 오픈 소스 Apache Spark 4.2로 확장되어, 더 넓은 생태계에 표준화되고 순서가 맞지 않는(out-of-order) 변경 데이터 캡처를 제공합니다.

변경 데이터 캡처(CDC)는 데이터 엔지니어가 Spark에서 구축하는 가장 일반적인 작업 중 하나이며, 수동으로 올바르게 구현하기 가장 까다로운 작업 중 하나입니다. 이전 포스트인 Stop hand-coding change data capture pipelines에서는 Apache™ Spark 선언적 파이프라인(SDP)의 AUTO CDC가 몇 줄의 간단한 선언만으로 수백 줄의 불안정한 MERGE 로직을 대체하여 SCD Type 1, SCD Type 2 및 스냅샷 CDC를 자동화하는 방법을 소개해 드렸습니다.

파이프라인 요구사항이 진화함에 따라, 엔지니어들은 기존의 표준 CDC 패턴으로는 해결하기 어려운 상황에 직면하게 됩니다.

  • 순서가 맞지 않는 이중 시간축(bitemporal) 타임라인 처리
  • 기존 데이터를 손상시키지 않고 부분 레코드 업데이트 처리
  • 스토리지 보존 기간보다 더 오래 지속되는 감사 가능성 유지

오늘 저희는 이러한 실제 까다로운 과제들을 해결하기 위해 AUTO CDC를 한 단계 더 발전시키고, 이 기능을 오픈 소스 Apache Spark 4.2로 확장합니다.

이중 시간축(Bitemporal) AUTO CDC를 통한 이중 축 이력 추적

표준 SCD Type 2 테이블은 실제 세계에서 사실이 언제 변경되었는지는 알려줄 수 있지만, 특정 시점에 시스템이 무엇을 기록하고 있었는지는 알려주지 못합니다.

SEC 규정 17a-4 및 FINRA 기록 보존 규정에 따라 기업은 특정 시점에 존재했던 그대로 기록을 재구성할 수 있어야 합니다. 2021년 이후 SEC의 기록 보존 단속으로 인해 100개 이상의 기업에서 20억 달러가 넘는 벌금이 부과되었습니다. 어려운 점은 오늘의 값을 저장하는 것이 아닙니다. 몇 달이 지난 후에 보고 일자의 참조 데이터가 무엇이었는지, 그리고 당시 우리 시스템이 무엇을 기록하고 있었는지 답변하는 것입니다.

표준 SCD Type 2는 사실이 변경된 시점이라는 하나의 타임라인만 추적합니다. 반면 이중 시간축(Bitemporal) AUTO CDC는 두 개의 타임라인을 독립적으로 추적합니다.

  • 비즈니스 시간(이벤트 또는 유효 시간): 실제 세계에서 사실이 실제로 참이었던 시점입니다. 예를 들어, 월요일에 주식 기호가 보고 가능해졌거나 분기 말에 국가 코드가 폐기된 경우입니다.
  • 시스템 시간(트랜잭션 또는 처리 시간): 기록 시스템이 데이터를 인식한 시점입니다. 월요일의 변경 사항이 수요일이 되어서야 파이프라인에 도달할 수 있습니다.

각 대상 테이블에는 시스템이 관리하는 4개의 컬럼이 생성됩니다. 비즈니스 시간을 위한 __START_AT__END_AT, 시스템 시간을 위한 __SYSTEM_START_AT__SYSTEM_END_AT입니다. 하나의 논리적 사실은 비즈니스 버전과 시스템 버전의 조합당 하나씩 여러 개의 물리적 행을 가질 수 있으며, 이를 통해 어느 축으로든 특정 시점의 재구성이 가능해집니다. 핵심적인 동작 보장 사항은 이벤트가 두 타임라인 중 어디에서나 어떤 순서로든 도착할 수 있다는 점입니다.

이미 처리된 데이터보다 더 이른 비즈니스 시간 또는 시스템 시간을 가진 수정 사항이 나타나면, 엔진은 끝에 단순히 추가하는 대신 영향을 받는 이력을 다시 작성합니다. 직접 작성하는 로직 없이 두 개의 시퀀싱 컬럼을 선언하기만 하면 엔진이 두 구간을 모두 유지합니다. 이는 심볼 마스터와 같은 차원 테이블뿐만 아니라 엄격한 감사 가능성이 필요한 거래 이력이나 센서 판독값과 같은 팩트 테이블에서도 동일하게 잘 작동합니다. FINRA CAT 참조 데이터를 대상으로 적용하면 다음과 같습니다.

정확한 SQL 절은 STORED AS SCD TYPE BITEMPORAL이 아니라 STORED AS BITEMPORAL이며, SEQUENCE BYSYSTEM SEQUENCE BY가 모두 필요합니다. 예를 들어, Acme의 보고 가능 플래그가 1월 1일(비즈니스 시간)에 변경되었지만 피드에는 1월 5일(시스템 시간)이 되어서야 수신되었다고 가정해 보겠습니다. 그 후 1월 8일에 실제 변경일이 1월 1일이지만 다른 값이라는 소급 수정 사항이 도착합니다. 이중 시간축(Bitemporal) AUTO CDC는 두 가지 질문에 모두 답할 수 있습니다.

1월 3일에 첫 번째 쿼리는 아무것도 반환하지 않는데, 이는 당시 시스템에 표시된 내용에 대한 정확하고 감사 가능한 답변입니다. 오늘 실행된 두 번째 쿼리는 수정된 사실을 반영합니다. 두 개의 시계, 두 개의 답변, 모두 정답입니다. 시퀀싱 컬럼은 정렬 가능한 유형이어야 하며 NULL 시퀀싱 값을 가질 수 없습니다. 이 기능은 서버리스 SDP 또는 Pro/Advanced 제품 에디션에서 실행되며 현재 베타 버전이므로 파이프라인을 채널 PREVIEW에 고정하세요.

타임 트래블을 넘어서: VACUUM에서도 살아남는 재현 가능한 ML

모델이 참조 데이터나 피처 데이터로 학습될 때, 재현 가능성이란 몇 달 후 검토나 감사 중에 모델이 사용한 정확한 데이터 세트를 재구성할 수 있음을 의미합니다. 직관적으로 Delta Lake 타임 트래블을 떠올리기 쉽지만, 이는 테이블의 파일 이력 속성일 뿐 영구적인 기록은 아닙니다. VACUUM은 최근 버전에서 더 이상 참조하지 않는 데이터 파일을 영구적으로 삭제하므로, 기본 7일 보존 기간이 지나면 학습 시점에 기록된 TIMESTAMP AS OF이 조용히 작동을 멈출 수 있습니다. 이중 시간축 테이블은 이 이력을 파일 버전이 아닌 데이터로 저장합니다. VACUUMOPTIMIZE은 파일을 압축하지만 논리적 이력은 절대 건드리지 않으므로, 과거의 모든 비즈니스 또는 시스템 버전은 여전히 쿼리 가능한 행으로 남아 있습니다. 이를 통해 재현 가능성을 확보하는 방법은 두 가지가 있습니다. 두 개의 기준 시점(비즈니스 시간 및 시스템 시간)을 MLflow 파라미터로 기록하고, 학습 쿼리를 해당 상태에 고정하는 것입니다.

또는 테이블이 현재 뷰를 노출하는 경우, 학습 시점에 단일 시스템 시점을 기록하고 나중에 해당 타임스탬프의 시스템 시간 쿼리로 재구성할 수 있습니다.

어느 쪽이든 재현 가능성 보장은 MLflow 실행의 몇 가지 타임스탬프에 불과하며, 이중 시간축 이력은 행으로 저장되기 때문에 VACUUM이 기본 파일을 정리한 후에도 해당 보장은 유효합니다.

AutoCDC 부분 업데이트 정식 출시(GA)

모든 변경 데이터 캡처(CDC) 소스가 업데이트 시 전체 행을 내보내는 것은 아닙니다. 대신 많은 소스가 변경된 필드만 전송하고 다른 모든 컬럼은 NULL로 표시합니다. 특별한 처리가 없으면 이러한 NULL 값이 대상 테이블의 기존 데이터를 의도치 않게 덮어쓸 수 있습니다. 지금까지 고객은 이러한 동작을 해결하기 위해 커스텀 로직을 구축해야 했습니다. 이제 AutoCDC 부분 업데이트를 통해 이 작업이 자동으로 처리됩니다.

부분 업데이트는 업데이트 이벤트가 컬럼의 하위 집합만 수정할 수 있도록 허용하여 AutoCDC를 확장합니다. 선택한 컬럼의 경우, 수신되는 업데이트의 NULL 값은 기존 값을 덮어쓰는 대신 "업데이트 안 함"으로 해석됩니다.

이는 변경되지 않은 값을 NULL로 내보내어 생략하는 CDC 소스에 특히 유용합니다. 부분 업데이트가 없으면 이러한 NULL이 대상 테이블의 기존 데이터를 덮어쓰게 됩니다.

예를 들어, 대상 테이블에 (1, 'A', 20)이 포함되어 있다고 가정해 보겠습니다.

수신되는 업데이트 이벤트에는 다음이 포함됩니다: (1, NULL, 30)

기본적으로 AutoCDC는 행을 다음과 같이 업데이트합니다: (1, NULL, 30).

부분 업데이트(Partial Updates)를 활성화하면 name의 NULL은 "기존 값을 변경하지 않고 그대로 유지"로 처리되어 다음과 같은 결과가 발생합니다: (1, 'A', 30).

부분 업데이트를 활성화하려면 AutoCDC 정의에 매개변수를 추가하기만 하면 됩니다. 어떤 열을 부분 업데이트로 처리할지 지정하는 세 가지 방법 중에서 선택할 수 있습니다:

  1. NULL 값을 무시해야 하는 열 목록:
    IGNORE NULL UPDATES ON columnList
  2. NULL 값을 무시하지 않아야 하는 열 목록:
    IGNORE NULL UPDATES ON * EXCEPT (columnList)
  3. 행마다 다를 수 있는 소스 열 이름:
    COLUMNS TO UPDATE

전체 구문, 예제 및 사용 안내는 부분 업데이트 적용 설명서를 참조하세요.

오픈 소스에 대한 지속적인 기여

Spark Declarative Pipelines는 오픈 소스이므로 가장 널리 사용되는 흐름 유형도 오픈 소스여야 합니다. 저희는 Apache Spark 4.2에 AUTO CDC Type 1용 Python API를 기여하는 것부터 시작하고 있습니다.

저희는 일회성 코드 배포가 아니라, 검토된 제안서와 풀 리퀘스트(PR)의 연속이라는 Spark의 다른 진화 방식과 동일한 방식으로 기여했습니다(SPIPSPARK-56249 참조).

순서가 맞지 않는 데이터에 대한 정확성이 기본적으로 제공됩니다. 소규모 보조 테이블이 삭제 톰스톤(delete tombstone)과 같이 일찍 도착하는 이벤트의 상태를 추적하고, 재시도된 마이크로 배치는 대상을 손상시키는 대신 수렴하며, 스토리지 형식이 아닌 Spark의 스트리밍 및 테이블 추상화를 기반으로 구축되었기 때문에 Delta Lake와 Apache Iceberg 모두에서 실행됩니다.

오픈 소스 커뮤니티에서 진행될 향후 계획은 다음과 같습니다:

  • 다음 릴리스 기능: 이미 SQL 인터페이스(CREATE FLOW ... AS AUTO CDC INTO)를 마스터에 병합했으며, 이는 다음 Apache Spark 릴리스에 포함될 예정입니다.
  • 고급 파이프라인 의미 체계(Semantics): SCD Type 2 전체 이력 관리, 네이티브 변경 로그(changelog) 입력, NULL 값이 대상 데이터를 덮어쓰지 않도록 방지하는 부분 업데이트 지원 기능이 개발 중입니다.
  • 신뢰성 및 테스트: 순서가 맞지 않는 데이터 및 멱등성(idempotent) 재시도에 대한 자동화된 테스트 제품군을 확장하는 동시에 apply-as-truncate 기능을 추가하고 있습니다.

시작하기

이중 시간(bitemporal) 규정 준수를 구현하거나, 부분 업데이트를 설정하거나, Apache Spark에서 오픈 소스 AutoCDC를 탐색하려는 경우, 아래 리소스를 확인하여 시작해 보세요.

(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)

최신 게시물을 이메일로 받아보세요

블로그를 구독하고 최신 게시물을 이메일로 받아보세요.