Trino 로 Iceberg 테이블 유지보수 — OPTIMIZE, 스냅샷 만료, 고아 파일 제거
Iceberg 테이블은 운영하면서 점점 느려집니다. 작은 파일 누적, 스냅샷 폭증, 고아 파일이 원인입니다. Trino 의 ALTER TABLE ... EXECUTE 프로시저로 컴팩션·스냅샷 만료·고아 파일 정리를 자동화하는 법과 메타데이터 테이블로 상태를 진단하는 법을 정리합니다.
Iceberg 테이블은 만들 때는 빠르지만, 운영하면서 서서히 느려집니다. 스트리밍 적재나 잦은 INSERT/MERGE 가 쌓이면 작은 파일이 폭증하고, 매 쓰기마다 새 스냅샷이 생기며, 만료된 데이터 파일이 스토리지에 고아로 남습니다. 이걸 방치하면 쿼리 플래닝이 수십 초로 늘고 스토리지 비용이 새어 나갑니다.
다행히 Trino 는 이 유지보수 작업을 SQL 한 줄로 실행할 수 있는 프로시저를 제공합니다. 이 글은 무엇이 테이블을 느리게 만드는지, 그리고 OPTIMIZE / expire_snapshots / remove_orphan_files 로 어떻게 정리하는지를 진단부터 자동화까지 정리합니다.
1. Iceberg 테이블이 느려지는 세 가지 원인
① 작은 파일(Small Files) → 파일마다 메타·footer I/O, 플래닝 폭증
② 스냅샷 누적(Snapshots) → metadata 파일 비대, time travel 이력 과다
③ 고아 파일(Orphan Files) → 더 이상 참조 안 되는 데이터가 스토리지에 잔존| 원인 | 증상 | 해결 프로시저 |
|---|---|---|
| 작은 파일 | 데이터는 적은데 쿼리 플래닝이 느림 | EXECUTE optimize |
| 스냅샷 누적 | metadata.json 비대, 플래닝 지연 | EXECUTE expire_snapshots |
| 고아 파일 | 스토리지 용량이 실제 데이터보다 큼 | EXECUTE remove_orphan_files |
2. 먼저 진단 — 메타데이터 테이블
Trino 는 Iceberg 테이블의 내부 상태를 "테이블$메타테이블" 형태로 조회하게 해줍니다. 정리 전에 현황을 파악하세요.
-- 데이터 파일 수와 총 용량 (작은 파일 진단)
SELECT count(*) AS file_count,
sum(file_size_in_bytes) / (1024*1024*1024) AS total_gb,
avg(file_size_in_bytes) / (1024*1024) AS avg_file_mb
FROM iceberg.analytics."events$files";
-- 스냅샷 개수와 이력 (스냅샷 누적 진단)
SELECT count(*) AS snapshot_count,
min(committed_at) AS oldest,
max(committed_at) AS newest
FROM iceberg.analytics."events$snapshots";
-- 파티션별 파일 분포 (특정 파티션만 작은 파일이 몰렸는지)
SELECT partition, file_count, total_size
FROM iceberg.analytics."events$partitions"
ORDER BY file_count DESC
LIMIT 20;주요 메타데이터 테이블:
| 테이블 | 내용 |
|---|---|
$files | 데이터 파일 목록·크기·통계 |
$snapshots | 스냅샷 이력·커밋 시각 |
$partitions | 파티션별 파일 수·행 수·크기 |
$manifests | manifest 파일 목록 |
$history | 테이블 상태 변경 이력 |
진단 기준:
avg_file_mb가 수 MB 이하로 낮고file_count가 수만수십만이면 작은 파일 문제,수천이면 스냅샷 누적 문제입니다.snapshot_count가 수백
3. OPTIMIZE — 작은 파일 컴팩션
작은 파일들을 모아 적정 크기(기본 목표 약 512MB)의 큰 파일로 병합합니다.
-- 테이블 전체 컴팩션
ALTER TABLE iceberg.analytics.events EXECUTE optimize;
-- 특정 파티션만 (권장: 최근 적재 구간만)
ALTER TABLE iceberg.analytics.events EXECUTE optimize
WHERE event_time >= TIMESTAMP '2026-06-01 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-06-05 00:00:00 UTC';
-- 특정 크기 미만 파일만 대상으로 (file_size_threshold)
ALTER TABLE iceberg.analytics.events EXECUTE optimize(file_size_threshold => '128MB');핵심 포인트:
- WHERE 로 범위를 좁히세요. 전체 컴팩션은 비싸고 오래 걸립니다. 스트리밍 적재라면 "어제~오늘" 파티션만 매일 컴팩션하는 식이 효율적입니다.
file_size_threshold보다 작은 파일만 대상으로 잡으면, 이미 큰 파일은 건드리지 않아 비용이 절감됩니다.- 컴팩션은 새 스냅샷을 생성합니다. 이전 작은 파일들은 즉시 삭제되지 않고 고아 후보가 되므로, 컴팩션 후 스냅샷 만료 + 고아 파일 제거를 이어서 하는 것이 정석입니다.
정렬을 곁들인 컴팩션
테이블에 sorted_by 가 설정돼 있으면 OPTIMIZE 가 데이터를 정렬해 다시 씁니다. 정렬은 Parquet min/max 통계 효과를 키워 프루닝을 개선합니다.
ALTER TABLE iceberg.analytics.events SET PROPERTIES sorted_by = ARRAY['user_id'];
ALTER TABLE iceberg.analytics.events EXECUTE optimize;4. expire_snapshots — 스냅샷 만료
Iceberg 는 매 쓰기마다 스냅샷을 남깁니다(time travel·롤백용). 오래 쌓이면 metadata 파일이 커지고, 만료 전까지는 옛 데이터 파일도 삭제되지 않습니다.
-- 7일보다 오래된 스냅샷 만료
ALTER TABLE iceberg.analytics.events EXECUTE expire_snapshots(retention_threshold => '7d');동작:
retention_threshold보다 오래된 스냅샷을 제거하고, 그 스냅샷에서만 참조되던 데이터 파일을 실제로 삭제합니다.- 즉 expire_snapshots 가 OPTIMIZE 가 남긴 옛 작은 파일들을 실제로 회수하는 단계입니다.
- 너무 짧게 잡으면 time travel·롤백 여력이 사라지므로, 운영 정책(보통 3~14일)에 맞추세요.
-- time travel: 만료 전이라면 과거 시점 조회 가능
SELECT * FROM iceberg.analytics.events
FOR TIMESTAMP AS OF TIMESTAMP '2026-06-03 00:00:00 UTC';주의:
retention_threshold는 보통 클러스터 기본 최소값(예: 7일) 이하로는 낮추지 못하도록 안전장치가 걸려 있습니다. 더 짧게 강제하려면 테이블/카탈로그 설정을 조정해야 하며, 데이터 보호 관점에서 권장하지 않습니다.
5. remove_orphan_files — 고아 파일 제거
스토리지에는 있지만 어떤 스냅샷도 참조하지 않는 파일(고아)을 삭제합니다. 비정상 종료된 쓰기, 실패한 커밋, 외부 도구의 잔여물 등이 원인입니다.
ALTER TABLE iceberg.analytics.events EXECUTE remove_orphan_files(retention_threshold => '7d');retention_threshold보다 오래된 고아 파일만 제거합니다. 이 값을 너무 짧게 잡으면, 지금 막 커밋 중인(아직 메타데이터에 반영 전인) 파일을 고아로 오인해 삭제할 수 있으므로 반드시 넉넉히(기본 7일 권장) 둡니다.- 동시에 쓰기 작업이 진행 중일 때 너무 공격적으로 돌리지 마세요. 쓰기가 한가한 시간대에 실행하는 것이 안전합니다.
6. 권장 유지보수 순서
세 작업은 순서가 중요합니다.
① OPTIMIZE 작은 파일 → 큰 파일 (새 스냅샷 생성, 옛 파일은 고아 후보화)
↓
② expire_snapshots 오래된 스냅샷 제거 → 더 이상 참조 안 되는 데이터 파일 실제 삭제
↓
③ remove_orphan_files 메타데이터가 모르는 잔여 파일까지 회수-- 일일 유지보수 묶음 (최근 파티션 컴팩션 + 정리)
ALTER TABLE iceberg.analytics.events EXECUTE optimize
WHERE event_time >= current_timestamp - INTERVAL '2' DAY;
ALTER TABLE iceberg.analytics.events EXECUTE expire_snapshots(retention_threshold => '7d');
ALTER TABLE iceberg.analytics.events EXECUTE remove_orphan_files(retention_threshold => '7d');7. 자동화 — 무엇을 얼마나 자주
워크로드별 권장 주기입니다.
| 작업 | 스트리밍/잦은 적재 | 일배치 적재 | 거의 정적 |
|---|---|---|---|
| OPTIMIZE | 매일(최근 파티션) | 매일/주간 | 월간 |
| expire_snapshots | 매일 | 주간 | 월간 |
| remove_orphan_files | 주간 | 주간 | 월간 |
자동화 방법:
- 스케줄러(Airflow/cron) 에서 위 SQL 묶음을 테이블별로 실행.
- 컴팩션은 별도의 유지보수용 Resource Group(낮은 동시성)에 배정해, 분석 쿼리 자원을 침범하지 않게 합니다.
- 큰 테이블은 한 번에 전체를 돌리지 말고 파티션 범위를 나눠 점진적으로.
8. 흔한 함정
| 함정 | 결과 | 회피 |
|---|---|---|
| OPTIMIZE 만 하고 스냅샷 만료 안 함 | 옛 작은 파일이 안 지워져 스토리지 그대로 | 항상 expire_snapshots 동반 |
| retention_threshold 너무 짧게 | 진행 중 파일 오삭제 / time travel 상실 | 7일 이상 권장 |
| 전체 OPTIMIZE 를 매일 | 비용·시간 폭발 | WHERE 로 최근 파티션만 |
| 쓰기 피크에 유지보수 | 커밋 충돌, 자원 경합 | 한가한 시간대 + 전용 Resource Group |
| remove_orphan_files 를 분석 클러스터에서 공격적으로 | I/O 부하 | 주간·저부하 시간 |
9. 정리
| 프로시저 | 해결 | 핵심 옵션 |
|---|---|---|
EXECUTE optimize | 작은 파일 병합(+정렬) | WHERE, file_size_threshold |
EXECUTE expire_snapshots | 스냅샷·옛 파일 정리 | retention_threshold |
EXECUTE remove_orphan_files | 미참조 잔여 파일 회수 | retention_threshold |
Iceberg 테이블 유지보수의 핵심은 세 가지를 순서대로, 정기적으로, 범위를 좁혀서 돌리는 것입니다. $files·$snapshots 메타데이터 테이블로 현황을 먼저 진단하고, 스트리밍 테이블은 최근 파티션 컴팩션을 매일, 스냅샷·고아 정리를 주기적으로 자동화하세요. 이 루틴만 갖춰도 "시간이 지날수록 느려지는" Iceberg 의 고질병을 구조적으로 막을 수 있습니다.
이 글은 Trino 440번대 + Iceberg spec v2 기준으로 작성되었습니다. Lakehouse 테이블 유지보수 자동화나 성능 저하 진단이 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀