Trino 로 Iceberg 테이블 유지보수 — OPTIMIZE, 스냅샷 만료, 고아 파일 제거
Iceberg 테이블은 운영하면서 점점 느려집니다. 작은 파일 누적, 스냅샷 폭증, 고아 파일이 원인입니다. Trino 의 ALTER TABLE ... EXECUTE 프로시저로 컴팩션·스냅샷 만료·고아 파일 정리를 자동화하는 법과 메타데이터 테이블로 상태를 진단하는 법을 정리합니다.
상용 서비스를 운영하다 보면 어느 순간 "분명히 데이터가 늘지도 않았는데 쿼리가 왜 이렇게 느리지?"라는 상황을 만나게 되죠. Iceberg 테이블도 똑같습니다. 처음 만들었을 때는 쌩쌩하지만, 운영하면서 서서히 내부가 지저분해집니다. 스트리밍 적재나 잦은 INSERT/MERGE 가 쌓이면 작은 파일이 폭증하고, 매 쓰기마다 새 스냅샷이 생기며, 만료된 데이터 파일이 스토리지에 고아로 남습니다. 이걸 방치하면 쿼리 플래닝이 수십 초로 늘고 스토리지 비용이 새어 나갑니다.
다행히 Trino 는 이 유지보수 작업을 SQL 한 줄로 실행할 수 있는 프로시저를 제공합니다. 이 글은 무엇이 테이블을 느리게 만드는지, 그리고 OPTIMIZE / expire_snapshots / remove_orphan_files 로 어떻게 정리하는지를 진단부터 자동화까지 함께 살펴봅니다.
이 글에서 배우는 것
- Iceberg 테이블 성능을 갉아먹는 세 가지 원인(작은 파일·스냅샷 누적·고아 파일)
- 메타데이터 테이블(
$files,$snapshots,$partitions)로 현황을 진단하는 방법EXECUTE optimize로 작은 파일을 병합하고 정렬까지 적용하는 방법expire_snapshots와remove_orphan_files로 스토리지를 실제로 회수하는 방법- 세 작업을 올바른 순서로 자동화하는 권장 패턴
1. Iceberg 테이블이 느려지는 세 가지 원인
Iceberg 가 느려지는 이유는 세 가지로 깔끔하게 정리됩니다. 각각이 어떤 증상을 만들고, 어떤 프로시저로 해결하는지 먼저 큰 그림을 잡아 봅시다.
| 원인 | 증상 | 해결 프로시저 |
|---|---|---|
| 작은 파일 | 데이터는 적은데 쿼리 플래닝이 느림 | 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 — 작은 파일 컴팩션
한 문장으로: 작은 파일 수백 개를 적정 크기의 큰 파일 몇 개로 합쳐 I/O 와 플래닝 비용을 한 번에 줄이는 작업입니다.
여러분의 테이블에 작은 파일이 수만 개라는 진단 결과가 나왔다면 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 — 스냅샷 만료
OPTIMIZE 로 파일을 병합했다고 해서 디스크 공간이 바로 줄어드는 건 아닙니다. 옛 작은 파일들은 아직 스냅샷에 참조된 채 남아 있거든요. 이 단계가 그 스냅샷들을 실제로 지워 공간을 돌려받는 과정입니다. 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 — 고아 파일 제거
스냅샷까지 정리했는데도 스토리지 용량이 예상보다 크다면 고아 파일이 범인일 수 있습니다. 비정상 종료된 쓰기, 실패한 커밋, 외부 도구의 잔여물 같은 것들이 스냅샷에는 등록되지 않은 채 스토리지에만 남아 있는 경우입니다. 이걸 고아(orphan) 파일이라 부르는데, 이 프로시저가 그것들을 깨끗하게 쓸어 담습니다.
ALTER TABLE iceberg.analytics.events EXECUTE remove_orphan_files(retention_threshold => '7d');retention_threshold보다 오래된 고아 파일만 제거합니다. 이 값을 너무 짧게 잡으면, 지금 막 커밋 중인(아직 메타데이터에 반영 전인) 파일을 고아로 오인해 삭제할 수 있으므로 반드시 넉넉히(기본 7일 권장) 둡니다.- 동시에 쓰기 작업이 진행 중일 때 너무 공격적으로 돌리지 마세요. 쓰기가 한가한 시간대에 실행하는 것이 안전합니다.
6. 권장 유지보수 순서
세 작업을 각각 이해했다면 이제 순서가 중요합니다. 순서를 바꾸면 기대한 효과가 나지 않거나 데이터를 잘못 지울 수 있으니 아래 흐름을 지켜 봅시다.
-- 일일 유지보수 묶음 (최근 파티션 컴팩션 + 정리)
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 의 고질병을 구조적으로 막을 수 있습니다.
마치며 — 핵심 요약
여기까지 따라왔다면 Iceberg 테이블 유지보수의 핵심을 이미 손에 쥔 셈입니다. 한번 더 정리해 볼게요.
- 세 가지 원인 — 작은 파일·스냅샷 누적·고아 파일이 Iceberg 성능 저하의 거의 전부입니다.
- 진단 먼저 —
$files·$snapshots·$partitions메타데이터 테이블로 현황을 확인한 뒤 처방을 내리세요. - 순서 지키기 —
OPTIMIZE→expire_snapshots→remove_orphan_files순으로 실행해야 각 단계 효과가 제대로 나타납니다. - 범위를 좁히기 — 전체 테이블 OPTIMIZE는 비용이 큽니다. WHERE 절로 최근 파티션만 대상으로 삼는 것이 훨씬 효율적입니다.
- retention_threshold 는 넉넉하게 — 7일 이하로 줄이면 time travel 여력이 사라지고, 진행 중인 커밋이 고아로 오삭제될 수 있습니다.
- 자동화로 구조적으로 막기 — Airflow 나 cron 으로 이 루틴을 정기 실행하면 "시간이 지날수록 느려지는" 고질병을 구조적으로 예방할 수 있습니다.
이 루틴 하나만 갖춰도 Iceberg 테이블은 오래 써도 처음 성능을 유지할 수 있습니다. 유지보수 자동화나 성능 저하 진단에 궁금한 점이 생기면 언제든 편하게 문의해 주세요!
이 글은 Trino 440번대 + Iceberg spec v2 기준으로 작성되었습니다. Lakehouse 테이블 유지보수 자동화나 성능 저하 진단이 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀