이전 글에서 Apache Iceberg의 계층적 메타데이터 아키텍처를 살펴보았다. 이번 글에서는 Iceberg의 가장 강력한 경쟁자인 Delta Lake를 다룬다. 같은 문제를 풀지만, 접근 방식이 다르다.
Delta Lake란?
Databricks에서 개발한 오픈소스 스토리지 레이어로, 데이터 레이크에 ACID 트랜잭션, 스키마 관리, 타임 트래블을 제공한다. Linux Foundation이 관리하며, Apache Spark과 가장 깊게 통합되어 있다.
Iceberg가 벤더 중립 멀티 엔진을 지향한다면, Delta Lake는 Spark/Databricks 에코시스템에서의 최적 성능을 지향한다.
아키텍처: Transaction Log (_delta_log)
Delta Lake의 핵심은 _delta_log 디렉토리이다. 테이블에 대한 모든 변경 사항을 순서대로 기록한다.
my_table/
├── _delta_log/
│ ├── 00000000000000000000.json # 최초 커밋
│ ├── 00000000000000000001.json # 두 번째 커밋
│ ├── 00000000000000000002.json # 세 번째 커밋
│ ├── ...
│ └── 00000000000000000010.checkpoint.parquet # 10번째에 체크포인트
├── part-00000-xxx.parquet # 실제 데이터 파일
├── part-00001-xxx.parquet
└── ...
Iceberg가 Catalog → Metadata File → Manifest List → Manifest File이라는 계층적 트리를 사용하는 반면, Delta Lake는 순차적인 커밋 로그를 쌓는다. Git의 커밋 히스토리와 유사한 방식이다.
커밋 로그 (JSON)
각 JSON 파일은 하나의 트랜잭션(커밋)을 나타낸다.
{
"add": {
"path": "part-00000-xxx.parquet",
"size": 1024000,
"partitionValues": {"date": "2026-03-05"},
"modificationTime": 1709654400000,
"dataChange": true,
"stats": "{\"numRecords\":10000,\"minValues\":{\"amount\":0},\"maxValues\":{\"amount\":99999}}"
}
}
주요 액션 타입:
- add: 새 파일 추가
- remove: 파일 논리적 삭제
- metaData: 스키마/파티션 변경
- txn: 애플리케이션 트랜잭션 ID (멱등성 보장)
- protocol: 읽기/쓰기 프로토콜 버전
체크포인트 (Parquet)
커밋이 쌓이면 읽기 성능이 떨어진다. 이를 해결하기 위해 10번째 커밋마다 체크포인트 파일을 생성한다.
- 그 시점까지의 모든 액션을 하나의 Parquet 파일로 압축
- 읽기 시 체크포인트부터 시작하면 되므로 성능이 크게 향상된다
- 수천 개의 JSON 파일을 일일이 리플레이하지 않아도 된다
ACID 보장 메커니즘
- 읽기: 체크포인트 + 이후 JSON 파일들을 리플레이하여 일관된 스냅샷을 생성
- 쓰기: 새 로그 엔트리를 원자적으로 추가 (클라우드 스토리지의 put-if-absent 또는 rename 활용)
- 동시 쓰기: Optimistic Concurrency Control — 충돌 시 재시도
원리는 Iceberg의 atomic swap과 비슷하지만, 구현 방식이 다르다. Iceberg는 metadata.json 포인터를 교체하고, Delta Lake는 다음 번호의 JSON 파일을 원자적으로 생성한다.
Change Data Feed (CDF)
Delta Lake의 독보적인 기능이다. 테이블의 행 단위 변경 사항을 추적한다. CDC(Change Data Capture)를 스토리지 계층에서 네이티브로 지원하는 것이다.
-- CDF 활성화
ALTER TABLE orders SET TBLPROPERTIES ('delta.enableChangeDataFeed' = 'true');
-- 변경 데이터 읽기 (버전 2~5 사이의 변경)
SELECT * FROM table_changes('orders', 2, 5);
반환되는 추가 컬럼:
_change_type: insert, update_preimage, update_postimage, delete_commit_version: 변경이 발생한 커밋 버전_commit_timestamp: 커밋 시각
활용 사례:
- 다운스트림 테이블 증분 업데이트: Silver → Gold 변환 시, 변경된 행만 처리
- 실시간 동기화 파이프라인: 다른 시스템에 변경분만 전파
- 감사 로그: 누가, 언제, 무엇을 변경했는지 추적
Iceberg는 이런 기능을 별도로 구현해야 한다. CDC 파이프라인이 핵심이라면 Delta Lake가 유리한 이유이다.
UniForm (Universal Format)
Delta의 멀티 엔진 전략이다. Delta 테이블을 Iceberg 또는 Hudi로도 읽을 수 있게 하는 호환성 레이어이다.
ALTER TABLE orders SET TBLPROPERTIES (
'delta.universalFormat.enabledFormats' = 'iceberg'
);
- Delta가 커밋할 때 Iceberg 메타데이터도 함께 생성
- Iceberg를 지원하는 엔진(Trino, Snowflake 등)에서 같은 데이터를 읽을 수 있다
- 데이터 복사 없이 포맷 호환성을 확보한다
Iceberg가 네이티브 멀티 엔진을 지향한다면, Delta Lake는 UniForm으로 "우리 포맷을 쓰되, 다른 포맷으로도 읽을 수 있게" 하는 전략을 취한다. 접근이 다르지만 결과적으로 멀티 엔진 접근이 가능하다.
스키마 관리
Delta Lake는 두 가지 모드를 제공한다.
Schema Enforcement
스키마와 맞지 않는 데이터는 쓰기를 거부한다. 데이터 품질의 첫 번째 방어선이다.
Schema Evolution
새 컬럼이 감지되면 스키마를 자동 확장한다.
-- 자동 스키마 병합 활성화
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", "true")
-- 수동 스키마 변경
ALTER TABLE orders ADD COLUMNS (category STRING, discount DOUBLE);
Iceberg는 컬럼 ID 기반 매핑으로 더 안전한 스키마 진화를 제공한다. Delta Lake는 Spark 설정 기반으로 더 간편하게 처리한다.
타임 트래블
Transaction Log에 모든 커밋이 기록되어 있으므로, 과거 시점의 데이터를 조회할 수 있다.
-- 버전으로 조회
SELECT * FROM orders VERSION AS OF 5;
-- 타임스탬프로 조회
SELECT * FROM orders TIMESTAMP AS OF '2026-03-01';
-- 히스토리 확인
DESCRIBE HISTORY orders;
원리는 Iceberg와 같지만 구현이 다르다. Iceberg는 스냅샷 ID를 메타데이터에 저장하고, Delta Lake는 커밋 로그를 특정 버전까지 리플레이한다.
Z-ORDER 최적화
자주 쿼리하는 컬럼 기준으로 데이터를 물리적으로 재배치하여 파일 스킵 효율을 높인다.
OPTIMIZE orders ZORDER BY (user_id, created_at);
Z-ORDER는 다차원 데이터를 1차원으로 매핑하는 공간 채움 곡선(Space-Filling Curve)이다. 같은 user_id의 데이터가 같은 파일에 모이도록 재배치하면, WHERE user_id = 'abc' 쿼리가 읽어야 할 파일 수가 크게 줄어든다.
Iceberg는 매니페스트의 컬럼 통계(min/max)로 파일 프루닝을 하지만, Delta Lake는 여기에 Z-ORDER를 추가하여 물리적 데이터 배치까지 최적화한다.
Iceberg와의 비교
| 항목 | Delta Lake | Apache Iceberg |
|---|---|---|
| 메타데이터 저장 | Transaction Log (JSON + Parquet) | 계층적 매니페스트 (Avro) |
| 동시성 제어 | Optimistic Concurrency | Optimistic Concurrency |
| 파티셔닝 | 명시적 (Hive 스타일) | Hidden Partitioning (자동) |
| 엔진 호환 | Spark 최적, UniForm으로 확장 | 네이티브 멀티 엔진 |
| CDC | Change Data Feed (네이티브) | 별도 구현 필요 |
| 파일 스킵 | Data Skipping + Z-ORDER | 컬럼 통계 + 매니페스트 프루닝 |
| 대규모 테이블 | 체크포인트로 관리 | 매니페스트 재사용으로 최적화 |
Delta Lake가 유리한 경우: Databricks/Spark 중심 환경, CDC가 핵심, 기존 Spark 파이프라인이 많은 경우
Iceberg가 유리한 경우: 멀티 엔진 환경, 벤더 중립이 중요한 경우, 페타바이트 급 대규모 테이블
주요 채택 사례
- Databricks: Delta Lake가 기본 포맷
- Microsoft Fabric: OneLake의 기본 테이블 포맷
- Azure Synapse Analytics: 네이티브 Delta 지원
- T-Mobile, Comcast, Shell: 대규모 프로덕션 사용
정리
Delta Lake는 Transaction Log라는 단순하면서도 강력한 메커니즘으로 데이터 레이크에 ACID를 부여한다.
_delta_log: 순차적 JSON 커밋 로그 + 10번째마다 Parquet 체크포인트- Change Data Feed: 행 단위 변경 추적 — CDC 파이프라인의 핵심
- UniForm: Delta 포맷을 유지하면서 Iceberg/Hudi로도 읽기 가능
- Z-ORDER: 물리적 데이터 재배치로 파일 스킵 극대화
- Spark 최적 통합: Databricks 환경에서 최고의 성능
Iceberg와 Delta Lake는 같은 문제를 다른 방식으로 풀고 있으며, UniForm 등을 통해 점점 수렴하고 있다. 중요한 것은 어떤 포맷을 고르느냐보다, Lakehouse 아키텍처 자체의 이점을 확보하는 것이다.