스키마 진화와 동시성 제어
Delta Lake vs Iceberg 2026: Which Table Format Wins? — 미상 (실전 프로덕션 경험 기반 블로그, 2026) Delta Lake — How It Works 중 Concurrency control (p.3, 대조 맥락), Apache Iceberg Key Features 중 Schema evolution / Snapshot isolation (p.5)
이 모듈을 다 읽으면
- Iceberg가 컬럼 추가·삭제·이름변경·순서변경을 안전하게 지원하는 원리(필드 ID)를 설명할 수 있다
- Iceberg의 동시성 제어가 실제로는 Delta Lake와 마찬가지로 낙관적 동시성 제어(OCC)임을 설명할 수 있다
- 스냅샷 격리가 동시 읽기/쓰기 상황에서 보장하는 것과 격리 수준을 설정으로 조정할 수 있다는 것을 구분할 수 있다
Iceberg의 스키마 진화가 안전한 이유는 컬럼을 이름이나 위치가 아니라 영구적인 필드 ID로 추적하기 때문이다. 동시성 제어 측면에서 원문은 Delta Lake만 '낙관적 동시성'이라고 명시하고 Iceberg는 '스냅샷 격리'라고만 설명하는데, 실제로는 두 포맷 모두 카탈로그의 포인터를 원자적으로 교체(compare-and-swap)하는 낙관적 동시성 제어를 사용한다. 스냅샷 격리는 이 위에서 '읽기가 항상 일관된 시점의 데이터를 본다'는 보장을 설명하는 개념이며, Iceberg는 행 단위 연산에 대해 격리 수준을 serializable/snapshot 중에서 설정할 수도 있다.
필드 ID 기반 스키마 진화
원문은 Iceberg가 컬럼 추가·삭제·이름변경·순서변경을 완전한 하위 호환성과 함께 지원한다고 간단히 언급하지만, 이것이 가능한 근본 원리는 설명하지 않는다. Iceberg는 테이블 생성 시(또는 컬럼 추가 시) 모든 컬럼에 고유하고 영구적인 필드 ID를 부여하고, 이 ID를 Parquet/ORC/Avro 파일의 메타데이터에도 함께 기록한다.
읽을 때 Iceberg는 컬럼을 이름이나 파일 안의 물리적 위치가 아니라 이 필드 ID로 찾는다. 그 결과 컬럼 이름 변경은 스키마의 '이름-ID 매핑'만 바꾸면 되는 순수 메타데이터 연산이 되고, 컬럼 순서 변경도 물리적 파일을 전혀 건드리지 않는다. 컬럼을 추가하면 새 필드 ID가 발급되고, 그 필드 ID가 존재하지 않는 예전 파일을 읽을 때는 해당 컬럼에 null(또는 v3부터는 지정된 기본값)을 채워 넣는다. 컬럼을 삭제해도 그 필드 ID는 영구히 폐기(retire)되어 재사용되지 않으므로, 나중에 실수로 같은 이름의 새 컬럼을 추가해도 예전 데이터와 뒤섞이는 사고가 방지된다.
핵심 포인트
- Iceberg는 컬럼을 이름/물리적 위치가 아니라 영구적인 필드 ID로 식별한다
- 이름 변경·순서 변경은 물리 파일을 건드리지 않는 순수 메타데이터 연산이다
- 삭제된 필드 ID는 재사용되지 않아 예전 데이터와 새 컬럼이 뒤섞이는 사고를 구조적으로 방지한다
동시성 제어: 스냅샷 격리와 낙관적 동시성 — 정정
정정 — 원문은 Delta Lake 섹션에서만 '동시성 제어는 낙관적 동시성을 사용한다'고 명시적으로 설명하고, Iceberg 섹션에서는 '동시 읽기/쓰기에 대한 스냅샷 격리'라고만 서술한다. 이는 마치 두 포맷의 동시성 제어 메커니즘 자체가 다른 것처럼 읽히지만, 실제로는 Iceberg도 낙관적 동시성 제어(Optimistic Concurrency Control, OCC)를 사용한다.
구체적으로, 쓰기 작업은 현재 카탈로그가 가리키는 메타데이터를 베이스로 새 데이터 파일과 새 메타데이터 파일(및 매니페스트)을 스테이징한 뒤, 카탈로그의 '현재 메타데이터 포인터'를 원자적으로 교체(compare-and-swap)하려고 시도한다. 이때 다른 쓰기가 먼저 커밋되어 베이스가 이미 바뀌어 있으면 교체는 실패하고, 해당 쓰기는 재시도한다(겹치지 않는 파일에 대한 변경이라면 새 베이스 위에 재적용해 커밋을 재시도할 수 있다). 이는 Hive Metastore의 테이블 속성이든, AWS Glue든, REST 카탈로그의 커밋 API든 카탈로그 구현과 무관하게 Iceberg 커밋 프로토콜의 공통 동작이다.
'스냅샷 격리'는 이 커밋 메커니즘 위에서 독자가 무엇을 보게 되는지를 설명하는 개념이다 — 읽기는 항상 어느 한 스냅샷의 일관된 상태를 보고, 쓰기 도중의 중간 상태를 절대 보지 않는다. 즉 낙관적 동시성 제어(쓰기 충돌을 어떻게 해결하는가)와 스냅샷 격리(읽기가 무엇을 보장받는가)는 서로 다른 층위의 개념이며, 두 포맷 모두 두 가지를 함께 갖추고 있다.
핵심 포인트
- Iceberg도 Delta Lake와 마찬가지로 카탈로그 포인터의 원자적 compare-and-swap에 기반한 낙관적 동시성 제어(OCC)를 사용한다
- 다른 쓰기가 먼저 커밋되어 베이스가 바뀌면 커밋이 실패하고, 겹치지 않는 변경이면 재시도로 해결한다
- 낙관적 동시성 제어(쓰기 충돌 해결 방식)와 스냅샷 격리(읽기 보장 수준)는 서로 다른 개념이며 두 포맷 모두 둘 다 갖고 있다
격리 수준의 한계와 설정 가능성
스냅샷 격리는 '읽기가 일관된 시점을 본다'는 것만 보장할 뿐, 동시에 들어온 두 쓰기가 서로 충돌하지 않는다는 것까지 보장하지는 않는다. 같은 데이터 파일을 동시에 수정하려는 두 트랜잭션 중 하나는 반드시 커밋에 실패하고 재시도해야 한다.
Iceberg는 행 단위 연산(MERGE, UPDATE, DELETE)에 대해 격리 수준을 serializable 또는 snapshot 중에서 테이블 속성으로 설정할 수 있다. 기본값은 대체로 serializable로, 동시에 커밋되는 다른 트랜잭션과 조건이 겹치면 더 적극적으로 충돌로 판단해 재시도를 유도한다. 이를 snapshot으로 낮추면 동시성(처리량)은 높아지지만, 특정 엔진·시나리오에서는 갱신 유실(lost update)에 더 취약해질 수 있다. 이런 트레이드오프가 존재한다는 사실 자체를 원문은 전혀 다루지 않는다.
핵심 포인트
- 스냅샷 격리는 읽기의 일관성만 보장하며, 쓰기 충돌 자체를 없애주지는 않는다
- Iceberg는 행 단위 연산의 격리 수준을 serializable/snapshot 중에서 설정할 수 있어 동시성과 안전성 사이 트레이드오프를 조정할 수 있다