전통적 데이터 레이크(Parquet + Hive 메타스토어)는 "디렉터리 = 테이블"이다. 엔진은 파티션 경로를 나열해 거기 있는 파일을 전부 테이블의 내용으로 본다. 이 단순함이 한계를 만든다.
원자성이 없다. 잡이 파일 10개 중 6개를 쓰고 죽으면, 그 6개가 그대로 테이블에 보인다. 쓰는 도중에 읽는 쿼리는 절반만 본다.
행 하나를 고치려면 파일을 통째로 다시 써야 한다. Parquet 은 수정할 수 없는 파일이다. 게다가 다시 쓰는 동안의 일관성을 보장할 방법이 없다.
스키마·파티션 변경이 위험하다. 컬럼 이름을 바꾸거나 파티션 체계를 바꾸면 기존 파일과 어긋난다.
과거 시점을 볼 수 없다. 덮어쓰면 이전 상태는 사라진다.
GDPR "삭제 권리" 대응이 어렵다. 특정 고객의 행만 지우려면 해당 파티션 전체를 재작성해야 한다.
오픈 테이블 포맷은 발상을 뒤집는다. 경로를 나열하지 않고, 메타데이터가 가리키는 파일만 테이블로 본다. 새 파일을 다 쓴 뒤 메타데이터 포인터를 한 번에 바꾸면(커밋) 그 순간 전체가 보이고, 실패하면 아무것도 보이지 않는다.
오픈 테이블 포맷의 ACID 는 스냅샷 기반이다. 쓰기는 새 데이터 파일과 새 메타데이터를 만든 뒤 "현재 스냅샷" 포인터를 원자적으로 교체한다. 읽기는 시작 시점의 스냅샷만 보므로 쓰기와 서로 막지 않는다(스냅샷 격리). 이전 스냅샷을 지우지 않고 남겨 두면 그것이 곧 시간 여행이다.
Glue Data Catalog 테이블 ── metadata_location ──► 메타데이터 파일 (스키마, 파티션 명세, 스냅샷 목록) │ ▼ 매니페스트 리스트 (스냅샷 1개 = 매니페스트들의 목록) │ ▼ 매니페스트 파일 (데이터 파일 목록 + 파일별 컬럼 통계) │ ▼ 데이터 파일(Parquet/ORC/Avro), 삭제 파일
카탈로그가 가진 것은 "최신 메타데이터 파일의 위치" 하나다. 커밋은 이 포인터를 새 파일로 바꾸는 일이다.
두 작성자가 동시에 커밋하면 한쪽만 성공한다(낙관적 동시성). 진 쪽은 최신 상태를 다시 읽어 재시도한다. Athena 는 Glue 카탈로그의 낙관적 잠금만 지원한다.
매니페스트에 파일별 통계가 있어 엔진은 S3 를 나열하지 않고 읽을 파일을 고른다. 파티션이 많아도 계획이 느려지지 않는 이유다.
숨은 파티셔닝(Iceberg): PARTITIONED BY (month(dt)) 처럼 변환 함수로 파티션을 정의한다. 쿼리는 WHERE dt >= ... 만 쓰면 되고, 엔진이 파티션 값을 계산해 프루닝한다. Hive 스타일처럼 사용자가 year=, month= 컬럼을 알아야 할 필요가 없다.
파티션 진화(Iceberg): 월 단위 파티션을 일 단위로 바꿔도 기존 파일을 다시 쓰지 않는다. 옛 파일은 옛 명세로, 새 파일은 새 명세로 읽는다.
Hudi 의 쿼리 타입: 스냅샷 쿼리(최신 상태), 증분 쿼리(특정 커밋 이후 바뀐 것만), 읽기 최적화 쿼리(MoR 에서 컴팩션된 부분만). 증분 쿼리가 하류 파이프라인을 증분으로 돌리게 해 준다.
Hudi 의 MoR 은 데이터를 컬럼형(Parquet) 기본 파일과 행 기반(Avro) 델타 로그로 나눠 저장하고, 컴팩션이 로그를 기본 파일에 합친다.
Athena 의 Iceberg UPDATE·DELETE·MERGE INTO 는 항상 Merge-on-Read 다. 위치 삭제 파일(position delete)을 쓰며, 테이블 속성에 CoW 를 지정해 두어도 무시한다. 그래서 Athena 로 자주 수정하는 테이블은 삭제 파일이 쌓이고, 주기적인 OPTIMIZE 가 필요하다.
⚠️ 함정: "MoR 테이블의 읽기가 점점 느려진다"의 답은 컴팩션이다. 스냅샷 만료(VACUUM)는 스토리지를 줄일 뿐 읽을 때 합쳐야 하는 삭제 파일을 없애 주지 않는다. 반대로 "스토리지 비용이 계속 는다"의 답이 스냅샷 만료다. 둘을 바꿔 고르지 않는다.
-- Athena (Iceberg): 행 수준 UPDATE / DELETEUPDATE sales.orders SET status = 'cancelled' WHERE order_id = 1001;DELETE FROM sales.orders WHERE customer_id = 'GDPR-REQ-42';-- MERGE (업서트): 소스 변경분을 타깃에 반영MERGE INTO sales.orders AS tUSING staging.order_changes AS sON t.order_id = s.order_idWHEN MATCHED THEN UPDATE SET status = s.statusWHEN NOT MATCHED THEN INSERT (order_id, status) VALUES (s.order_id, s.status);
-- 시간 여행: 과거 타임스탬프 / 스냅샷 ID 시점 조회 (Iceberg on Athena)SELECT * FROM sales.orders FOR TIMESTAMP AS OF TIMESTAMP '2026-06-20 00:00:00';SELECT * FROM sales.orders FOR VERSION AS OF 8623491172120013573;-- 스냅샷 목록 확인 (Iceberg 메타데이터 테이블)SELECT snapshot_id, committed_at, operation FROM "sales"."orders$snapshots";
DML 한 번이 스냅샷 하나를 만든다. 잘못 적재했으면 직전 스냅샷 시점으로 조회해 복구 데이터를 뽑을 수 있다.
시간 여행으로 돌아갈 수 있는 범위는 남아 있는 스냅샷까지다. 스냅샷을 만료시키면 그 이전으로는 못 간다.
⚠️ 함정: DELETE 를 실행했다고 데이터가 S3 에서 사라진 것이 아니다. 새 스냅샷에서 안 보일 뿐, 이전 스냅샷이 가리키는 데이터 파일은 그대로 있고 시간 여행으로 다시 읽힌다. GDPR 삭제 요청을 완전히 처리하려면 DELETE → 컴팩션(삭제 반영해 파일 재작성) → 스냅샷 만료(옛 파일 물리 삭제)까지 가야 한다. S3 버전 관리가 켜진 버킷이면 비현행 버전 정리 규칙도 필요하다.
AWS Glue 3.0 이상이 Hudi·Delta Lake·Iceberg 를 기본 지원한다. 별도 커넥터 설치 없이 잡 파라미터 --datalake-formats 에 iceberg, hudi, delta 를 지정한다.
EMR 은 Spark·Flink·Trino 등에서 세 포맷을 쓴다. 대규모 배치 업서트와 컴팩션 잡에 맞는다.
세 포맷 모두 Glue Data Catalog 를 카탈로그로 쓸 수 있어, 한 엔진이 쓴 테이블을 다른 엔진이 읽는다. 단 엔진마다 지원 범위가 다르다는 점은 위 표와 같다.
# Glue Spark 에서 Iceberg 테이블에 업서트 (잡 파라미터: --datalake-formats iceberg)spark.sql(""" MERGE INTO glue_catalog.sales.orders t USING updates s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT *""")
-- 컴팩션: 삭제 파일을 데이터 파일에 합치고 목표 크기에 가깝게 다시 쓴다OPTIMIZE sales.orders REWRITE DATA USING BIN_PACK WHERE dt >= DATE '2026-06-01';-- 스냅샷 보존 기간을 3일로 줄인 뒤 만료·고아 파일 제거ALTER TABLE sales.orders SET TBLPROPERTIES ('vacuum_max_snapshot_age_seconds' = '259200');VACUUM sales.orders;
Athena Iceberg 테이블 속성
기본값
의미
vacuum_max_snapshot_age_seconds
432000(5일)
이보다 오래된 스냅샷을 만료 대상으로 본다
vacuum_min_snapshots_to_keep
1
최소한 남길 스냅샷 수. 보존 기간보다 우선한다
optimize_rewrite_data_file_threshold
5
최적화가 필요한 데이터 파일이 이보다 적으면 다시 쓰지 않는다
optimize_rewrite_delete_file_threshold
2
데이터 파일에 딸린 삭제 파일이 이보다 적으면 다시 쓰지 않는다
OPTIMIZE 와 VACUUM 은 직접 실행해야 하는 쿼리다. 스케줄을 걸지 않으면 아무도 돌리지 않는다.
직접 돌리지 않으려면 Glue Data Catalog 의 테이블 최적화를 켠다. Iceberg 테이블에 대해 컴팩션, 스냅샷 보존, 고아 파일 삭제를 관리형으로 수행한다.
컴팩션도 새 스냅샷을 만든다. 컴팩션 직후에는 옛 파일과 새 파일이 함께 있어 스토리지가 잠시 늘어난다. 스냅샷 만료까지 해야 줄어든다.
스냅샷 보존 기간은 "얼마나 과거로 시간 여행할 수 있어야 하는가"와 "스토리지 비용" 사이의 선택이다.
S3 수명주기로 Iceberg 테이블의 데이터 프리픽스를 만료시키면 안 된다. 메타데이터는 그 파일이 있다고 믿는데 파일이 사라지면 쿼리가 실패한다. 테이블 포맷의 파일 수명은 테이블 포맷의 도구(스냅샷 만료·고아 파일 삭제)로 관리한다.