Trino + Iceberg 는 파티션 문제를 어떻게 해결하는가
Hive/Impala 에서 파티션 컬럼 누락 시 풀스캔이 발생하는 이유와, Trino + Iceberg 의 hidden partitioning·메타데이터 기반 프루닝이 이 문제를 구조적으로 제거하는 방법을 정리합니다.
Trino 에서 Parquet 포맷의 Iceberg 테이블을 만들고, 같은 데이터를 Impala 와 Hive 에서도 조회할 수 있게 구성하는 것은 흔한 아키텍처입니다. 그런데 Impala 나 Hive 에서 쿼리를 날릴 때 WHERE 절에 파티션 컬럼을 빠뜨리면 수 TB 풀스캔이 발생하고 클러스터가 멈추는 경험을 한 적 있으신가요? 이 글은 Hive/Impala 에서 이 문제가 왜 구조적으로 피할 수 없는지, 그리고 Trino + Iceberg 가 어떻게 이 문제를 근본적으로 제거하는지를 다룹니다.
1. 문제: 파티션 컬럼을 빠뜨리면 클러스터가 멈춘다
Hive 와 Impala 에서 파티션 테이블을 만드는 전형적인 패턴입니다.
CREATE TABLE events (
event_id BIGINT,
user_id BIGINT,
event_type STRING,
event_time TIMESTAMP,
city STRING,
payload STRING
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET;이 테이블에 1년치 데이터가 쌓여 있다고 가정합니다. 약 500GB, 365개 파티션.
-- 정상: 파티션 프루닝 동작, 하루치 약 1.4GB 만 스캔
SELECT * FROM events WHERE dt = '2026-04-12' AND user_id = 12345;
-- 문제: dt 파티션 컬럼 누락 → 365개 파티션 전체 스캔 (500GB)
SELECT * FROM events WHERE user_id = 12345;두 번째 쿼리는 user_id 조건만 있고 dt 가 없습니다. Hive/Impala 는 어떤 파티션에 user_id = 12345 데이터가 있는지 알 방법이 없으므로, 모든 파티션의 모든 파일을 열어서 확인해야 합니다.
2. 왜 Hive/Impala 는 이 문제를 피할 수 없는가
디렉터리 기반 파티셔닝
Hive/Impala 의 파티션은 물리 디렉터리입니다.
/warehouse/events/dt=2026-04-01/
/warehouse/events/dt=2026-04-02/
/warehouse/events/dt=2026-04-03/
...
/warehouse/events/dt=2026-04-12/
├── 000000_0.parquet
├── 000001_0.parquet
└── 000002_0.parquetHive Metastore(HMS)는 파티션 디렉터리 목록을 관리하지만, 각 파일 안에 어떤 데이터가 들어있는지(min/max 통계)는 모릅니다. 쿼리 플래너가 파티션을 줄이려면 WHERE 절에 파티션 컬럼(dt)이 명시적으로 있어야 합니다.
Strict Mode 로 막을 수는 있지만
Hive 는 hive.mapred.mode=strict, Impala 는 쿼리 옵션으로 파티션 컬럼이 없는 쿼리를 거부할 수 있습니다. 하지만 이것은 "실수를 차단" 하는 것이지, 파티션 컬럼 없이도 효율적으로 조회하는 것은 불가능합니다.
3. 파티션 개수 제한: Hive/Impala vs Trino+Iceberg
파티션을 잘게 나누면 쿼리 성능이 좋아지지만, Hive/Impala 에서는 파티션 수 자체가 병목이 됩니다.
Hive/Impala 의 파티션 제한
| 항목 | Hive | Impala |
|---|---|---|
| 파티션 컬럼 수 | 제한 없음 (실무상 3~4개 권장) | 동일 |
| 최대 파티션 수 | HMS 성능 한계로 수만~수십만 개에서 심각한 저하 | Catalogd 메모리에 파티션 메타를 전부 올리므로 수십만 개 넘으면 OOM·갱신 지연 |
| 동적 파티션 생성 제한 | hive.exec.max.dynamic.partitions 기본값 1000 | MAX_DYNAMIC_PARTITIONS 쿼리 옵션으로 제한 |
| 플래닝 시 파티션 로드 | HMS 에 RPC → 파티션 수가 많으면 쿼리 플래닝만 수 분 | HDFS NameNode + Catalogd 동기화 비용 추가 |
파티션 컬럼을 2~3개 조합하면 카디널리티가 폭발합니다. 예를 들어 dt × region × device_type = 365 × 50 × 10 = 182,500 파티션/년. Hive/Impala 에서는 이런 설계를 할 수 없습니다.
Trino + Iceberg 의 파티션 제한
| 항목 | Trino + Iceberg |
|---|---|
| 파티션 컬럼 수 | Iceberg spec 상 제한 없음 |
| 최대 파티션 수 | 사실상 무제한. 파티션이 HMS 레코드가 아니라 manifest 내부 메타데이터 |
| 동적 파티션 생성 제한 | 없음 |
| 플래닝 시 파티션 로드 | Manifest 파일만 읽으면 됨. HMS RPC 불필요 |
Iceberg 는 파티션 정보를 HMS 에 등록하지 않고 manifest 파일에 저장합니다. 따라서 파티션 수가 늘어나도 HMS 에 RPC 를 날리지 않으며, 100만 개 파티션이 있어도 manifest 프루닝으로 필요한 파일만 빠르게 필터링합니다.
비교 요약
| 항목 | Hive/Impala | Trino + Iceberg |
|---|---|---|
| 파티션 = | HMS 레코드 + HDFS 디렉터리 | Manifest 내부 메타데이터 |
| 파티션 수 실무 한계 | 수만~수십만 (이후 성능 저하) | 사실상 무제한 |
| 파티션 추가 비용 | HMS ALTER TABLE ADD PARTITION (RPC) | 데이터 쓰기 시 자동 반영 (manifest commit) |
| 파티션 수 × 플래닝 시간 | 선형 증가 | 거의 일정 |
| 동적 파티션 생성 제한 | 기본 1000개/쿼리 | 없음 |
실무 시사점: Hive/Impala 에서는 시간 파티션을 일 단위가 한계이지만, Trino + Iceberg 에서는 hours(event_time) 파티션도 부담 없고, days(event_time), bucket(32, user_id) 같은 복합 파티션도 자유롭습니다.
4. Iceberg 가 다른 점: 메타데이터 기반 아키텍처
Iceberg 는 테이블 메타데이터를 3계층 구조로 관리합니다.
Metadata file (JSON)
└── Snapshot
└── Manifest list (Avro)
└── Manifest file (Avro)
└── 개별 데이터 파일 정보
├── 파일 경로
├── 파티션 값
├── row count
├── 컬럼별 min / max
├── null count
└── 파일 크기핵심은 manifest file 입니다. 각 데이터 파일의 파티션 값과 컬럼별 min/max 통계가 manifest 안에 들어있으므로, 데이터 파일을 열지 않고도 "이 파일이 쿼리에 필요한지" 를 판단할 수 있습니다.
Hive/Impala 와의 근본적인 차이:
| 항목 | Hive/Impala | Iceberg |
|---|---|---|
| 메타데이터 위치 | HMS (외부 RPC) | Manifest 파일 (스토리지 직접 읽기) |
| 파일 수준 통계 | 없음 (파티션 단위만) | min/max, null count, row count 등 |
| 파일 프루닝 | 불가능 | Manifest 기반으로 가능 |
5. Hidden Partitioning — 사용자가 파티션을 몰라도 되는 구조
이것이 Iceberg 의 가장 큰 차별점입니다.
Hive 방식
-- 테이블 생성 시 파티션 컬럼을 스키마에 명시
CREATE TABLE events (...) PARTITIONED BY (dt STRING);
-- INSERT 시 파티션 값을 명시적으로 지정
INSERT INTO events PARTITION (dt = '2026-04-12') VALUES (...);
-- SELECT 시에도 파티션 컬럼을 WHERE 에 명시해야 프루닝
SELECT * FROM events WHERE dt = '2026-04-12';사용자가 dt 라는 파티션 컬럼의 존재를 알고, INSERT/SELECT 모두에서 명시해야 합니다.
Iceberg 방식: Partition Transform
-- 파티션 컬럼이 스키마에 노출되지 않음
CREATE TABLE iceberg_catalog.db.events (
event_id BIGINT,
user_id BIGINT,
event_type VARCHAR,
event_time TIMESTAMP(6) WITH TIME ZONE,
city VARCHAR,
payload VARCHAR
)
WITH (
format = 'PARQUET',
partitioning = ARRAY['days(event_time)']
);event_time 컬럼에서 자동으로 일 단위 파티션이 생성됩니다. 별도의 dt 파티션 컬럼이 스키마에 없습니다.
-- 사용자는 그냥 event_time 으로 조건을 걸면 됨
SELECT * FROM events
WHERE event_time >= TIMESTAMP '2026-04-12 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-04-13 00:00:00 UTC';Trino 가 days(event_time) partition transform 을 알고 있으므로, 자동으로 해당 일자의 파티션만 스캔합니다. 파티션 컬럼이 스키마에 없으니 빠뜨릴 일 자체가 없습니다.
지원되는 Partition Transform:
| Transform | 설명 | 예시 |
|---|---|---|
years(col) | 연 단위 | years(event_time) |
months(col) | 월 단위 | months(event_time) |
days(col) | 일 단위 | days(event_time) |
hours(col) | 시간 단위 | hours(event_time) |
bucket(N, col) | 해시 버킷 N개 | bucket(32, user_id) |
truncate(W, col) | 문자열/숫자를 W 폭으로 잘라서 파티션 | truncate(10, city) |
6. Trino + Iceberg 에서 파티션 컬럼이 꼭 필요한가?
결론: 필수는 아니다. 하지만 없으면 달라지는 것이 있다
Iceberg 테이블은 파티션 없이도 생성·조회가 가능합니다.
-- 파티션 없는 Iceberg 테이블
CREATE TABLE iceberg_catalog.db.events_no_partition (
event_id BIGINT,
user_id BIGINT,
event_time TIMESTAMP(6) WITH TIME ZONE,
city VARCHAR
)
WITH (format = 'PARQUET');이 테이블에 WHERE event_time = TIMESTAMP '2026-04-12 10:00:00 UTC' 로 조회하면:
- Manifest 에 파티션 값이 없음 → 모든 manifest entry 를 확인
- 하지만 Parquet footer 의 column statistics(min/max)로 row group 단위 스킵은 가능
- 결과: 파일은 전부 열지만, row group 단위로 일부 스킵 → Hive 풀스캔보다는 낫지만, 파티션이 있을 때보다 훨씬 느림
| 구성 | 파일 프루닝 | Row Group 프루닝 | 전체 스캔 범위 |
|---|---|---|---|
| 파티션 없음 | 불가 | Parquet min/max 로 일부 스킵 | 거의 전체 |
| 파티션 있음 (hidden) | Manifest 기반으로 대부분 스킵 | 추가로 row group 스킵 | 해당 파티션 파일만 |
파티션 컬럼이 아닌 컬럼을 WHERE 에 넣으면?
파티션은 days(event_time) 으로 설정했지만, 쿼리는 WHERE user_id = 12345 인 경우:
- Manifest 프루닝:
user_id는 파티션 transform 과 무관 → 파일을 줄일 수 없음 → 모든 데이터 파일이 후보 - Parquet min/max 프루닝: 각 파일의
user_id컬럼 min/max 확인 → 범위 밖이면 스킵 - 실제 읽기: min/max 범위 안에 드는 파일만 열어서 row group 단위로 한 번 더 필터
결과: 파티션 프루닝 효과는 0, Parquet 통계에만 의존. 데이터가 정렬되어 있지 않으면 사실상 풀스캔에 가까움.
대응법:
-- 방법 1: user_id 에 대한 bucket 파티션 추가
ALTER TABLE events SET PROPERTIES
partitioning = ARRAY['days(event_time)', 'bucket(64, user_id)'];
-- 방법 2: 정렬 키 설정으로 min/max 통계 효과 극대화
ALTER TABLE events SET PROPERTIES
sorted_by = ARRAY['user_id'];
ALTER TABLE events EXECUTE optimize;존재하지 않는 컬럼을 WHERE 에 넣으면?
SELECT * FROM events WHERE non_existent_column = 'value';
-- 결과: COLUMN_NOT_FOUND 에러로 즉시 쿼리 실패Iceberg 스키마에 없는 컬럼이므로 플래닝 단계에서 거부됩니다. 이 동작은 Hive/Impala 도 동일합니다.
단, Schema Evolution 으로 나중에 추가된 컬럼의 경우, 추가 이전의 데이터 파일에는 해당 컬럼이 없으므로 NULL 로 처리됩니다. 쿼리 자체는 정상 실행됩니다.
7. Trino 의 파일 프루닝 메커니즘
Trino + Iceberg 는 3단계 깔때기로 불필요한 데이터를 걸러냅니다.
[전체 데이터 파일]
│
▼
① Manifest 기반 프루닝
(파티션 값 + 파일 수준 min/max)
│ 불필요한 파일 제거
▼
② Parquet Row Group 프루닝
(row group 별 column statistics)
│ 불필요한 row group 스킵
▼
③ Dynamic Filtering
(JOIN 의 build side 결과를 probe side 에 전파)
│ 추가로 파일/row group 스킵
▼
[실제로 읽는 데이터]각 단계가 순차적으로 데이터를 줄이므로, 최종적으로 읽는 데이터량이 극적으로 감소합니다.
8. 실전 비교: 같은 데이터, 같은 쿼리, 다른 결과
시나리오 설정
- 테이블: 1년치 이벤트 로그, 약 500GB
- 파티션: 일 단위 (Hive:
dt컬럼, Iceberg:days(event_time)) - Parquet 포맷
쿼리 1: 파티션 컬럼 명시
-- Hive/Impala
SELECT * FROM events WHERE dt = '2026-04-12' AND user_id = 12345;
-- Trino + Iceberg
SELECT * FROM events
WHERE event_time >= TIMESTAMP '2026-04-12 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-04-13 00:00:00 UTC'
AND user_id = 12345;| 엔진 | 스캔 범위 | 비고 |
|---|---|---|
| Hive/Impala | ~1.4GB (1일분) | 파티션 프루닝 정상 동작 |
| Trino + Iceberg | ~1.4GB (1일분) | Hidden partition 프루닝 동작 |
모두 빠릅니다.
쿼리 2: 파티션 컬럼 누락 — 시간 범위만
-- Hive/Impala: dt 를 안 쓰고 event_time 으로만 조건
SELECT * FROM events WHERE event_time >= '2026-04-12' AND event_time < '2026-04-13';
-- Trino + Iceberg: 동일한 조건
SELECT * FROM events
WHERE event_time >= TIMESTAMP '2026-04-12 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-04-13 00:00:00 UTC';| 엔진 | 스캔 범위 | 비고 |
|---|---|---|
| Hive/Impala | ~500GB (전체) | event_time 은 파티션 컬럼이 아니므로 풀스캔 |
| Trino + Iceberg | ~1.4GB (1일분) | days(event_time) transform 으로 자동 프루닝 |
350배 차이. Hive/Impala 에서는 풀스캔, Trino + Iceberg 에서는 하루치만 읽습니다.
쿼리 3: 파티션과 무관한 컬럼
SELECT * FROM events WHERE user_id = 12345;| 엔진 | 스캔 범위 | 비고 |
|---|---|---|
| Hive/Impala | ~500GB (전체) | 풀스캔 |
Trino + Iceberg (파티션: days(event_time) 만) | ~500GB (전체) | user_id 는 파티션과 무관, Parquet min/max 에만 의존 |
Trino + Iceberg (파티션: days(event_time) + bucket(64, user_id)) | ~7.8GB (1/64) | bucket 파티션 프루닝 |
파티션 설계가 쿼리 패턴과 맞아야 효과가 있습니다.
쿼리 4: 파티션 없는 Iceberg 테이블
SELECT * FROM events_no_partition
WHERE event_time >= TIMESTAMP '2026-04-12 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-04-13 00:00:00 UTC';| 엔진 | 스캔 범위 | 비고 |
|---|---|---|
| Trino + Iceberg (파티션 없음) | 거의 전체 | Parquet min/max 로 일부 row group 스킵, 파일 프루닝 불가 |
| Trino + Iceberg (파티션 있음) | ~1.4GB | 파티션 프루닝으로 대부분 스킵 |
파티션 없는 Iceberg 도 Hive 풀스캔보다는 약간 낫지만, 파티션이 있을 때와는 10~50배 성능 차이가 납니다.
9. Iceberg 테이블 생성과 파티션 설정
기본 생성
CREATE TABLE iceberg_catalog.db.events (
event_id BIGINT,
user_id BIGINT,
event_type VARCHAR,
event_time TIMESTAMP(6) WITH TIME ZONE,
city VARCHAR,
payload VARCHAR
)
WITH (
format = 'PARQUET',
partitioning = ARRAY['days(event_time)']
);복합 파티션
CREATE TABLE iceberg_catalog.db.events (
event_id BIGINT,
user_id BIGINT,
event_type VARCHAR,
event_time TIMESTAMP(6) WITH TIME ZONE,
city VARCHAR
)
WITH (
format = 'PARQUET',
partitioning = ARRAY['days(event_time)', 'bucket(32, user_id)']
);Partition Evolution — 기존 데이터 재작성 없이 파티션 전략 변경
Iceberg 고유 기능입니다. Hive/Impala 에서는 불가능합니다.
-- 기존: 일 단위 파티션
-- 변경: 시간 단위 파티션으로 전환
ALTER TABLE events SET PROPERTIES
partitioning = ARRAY['hours(event_time)'];이후 새로 쓰이는 데이터만 시간 단위 파티션을 적용받고, 기존 데이터는 일 단위 파티션 그대로 유지됩니다. Iceberg 의 manifest 가 파티션 spec 버전을 추적하므로 혼재된 상태에서도 프루닝이 정상 동작합니다.
10. Trino + Iceberg 에서도 성능이 나빠지는 경우
Trino + Iceberg 가 만능은 아닙니다. 다음 상황에서는 성능이 크게 저하됩니다.
10.1 Small File Problem
Iceberg 는 파티션 수 제한이 없는 대신, INSERT 가 잦으면 파티션당 수백~수천 개의 작은 파일이 쌓입니다. 각 파일마다 manifest entry 가 존재하고, Parquet footer 를 파일마다 열어야 하므로 파일 수 × I/O 오버헤드가 발생합니다.
증상: 데이터 양은 적은데 쿼리 플래닝이 수십 초 걸림.
-- 작은 파일 병합 (컴팩션)
ALTER TABLE events EXECUTE optimize;
-- 또는 WHERE 조건으로 특정 파티션만 컴팩션
ALTER TABLE events EXECUTE optimize
WHERE event_time >= TIMESTAMP '2026-04-01 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-05-01 00:00:00 UTC';10.2 Manifest 비대화
파일 수가 수십만 개를 넘으면 manifest 자체를 읽는 시간이 늘어납니다.
-- 파일 수 확인
SELECT count(*) AS file_count,
sum(file_size_in_bytes) / (1024*1024*1024) AS total_gb
FROM "events$files";10.3 Predicate Pushdown 이 안 되는 WHERE 조건
WHERE 절에 컬럼을 함수로 감싸면 predicate pushdown 이 깨집니다.
| 패턴 | Pushdown | 설명 |
|---|---|---|
WHERE event_time = TIMESTAMP '...' | O | 직접 비교 |
WHERE CAST(event_time AS DATE) = DATE '...' | X | 함수 적용 → 풀스캔 |
WHERE year(event_time) = 2026 | X | 함수 래핑 → 풀스캔 |
WHERE LOWER(city) = 'seoul' | X | 문자열 함수 → 풀스캔 |
WHERE city LIKE '%eoul' | X | 앞쪽 와일드카드 → min/max 활용 불가 |
WHERE city IN ('Seoul', 'Busan') | O | IN 리스트 → pushdown 가능 |
WHERE event_time BETWEEN ... AND ... | O | 범위 비교 → pushdown + partition 프루닝 |
핵심 규칙: 컬럼은 항상 왼쪽에 단독으로, 변환은 오른쪽(상수 쪽)에서 처리.
-- BAD: pushdown 안 됨
WHERE CAST(event_time AS DATE) = DATE '2026-04-12'
-- GOOD: pushdown 됨
WHERE event_time >= TIMESTAMP '2026-04-12 00:00:00 UTC'
AND event_time < TIMESTAMP '2026-04-13 00:00:00 UTC'10.4 정렬되지 않은 데이터
Parquet row group 의 min/max 통계는 데이터가 정렬되어 있을 때 효과적입니다.
- 정렬된 경우: row group 1 은
user_id 1~1000, row group 2 는1001~2000→WHERE user_id = 500이면 row group 1만 읽음 - 비정렬 경우: 모든 row group 의
user_id범위가1~100000으로 겹침 → 전부 읽어야 함
-- 정렬 키 설정
ALTER TABLE events SET PROPERTIES
sorted_by = ARRAY['user_id'];
-- 기존 데이터 재정렬
ALTER TABLE events EXECUTE optimize;10.5 JOIN 시 Dynamic Filtering 미작동
Dynamic Filtering 이 작동하지 않는 조건:
- Build side 결과가 너무 클 때 (수십만 건 이상) → dynamic filter 생성 포기
LEFT JOIN/FULL OUTER JOIN→ probe side 를 줄일 수 없음- JOIN 키가 파티션 컬럼과 무관할 때
10.6 과도한 스냅샷 누적
Iceberg 는 매 쓰기마다 새 스냅샷을 생성합니다. 시간이 지나면 수천 개의 스냅샷이 쌓이고, metadata file 크기 증가로 플래닝이 느려집니다.
-- 오래된 스냅샷 만료
ALTER TABLE events EXECUTE expire_snapshots(retention_threshold => '7d');
-- 고아 파일 제거
ALTER TABLE events EXECUTE remove_orphan_files(retention_threshold => '7d');10.7 Worker 메모리 부족
파티션/파일 수와 무관하게, 대량 데이터를 집계·조인하면 Worker 메모리가 초과됩니다. Trino 는 디스크로 spill 가능하지만 성능이 크게 저하됩니다.
대응:
query.max-memory-per-node조정SELECT *지양, 필요한 컬럼만 지정- 대량 집계는 단계별로 분리
11. 성능 확보를 위한 추가 팁
| 항목 | 설정/명령 | 효과 |
|---|---|---|
| 통계 수집 | ANALYZE events | Trino 플래너가 더 나은 실행 계획 생성 |
| Row group 크기 | write.parquet.row-group-size-bytes | 적절한 크기로 row group 프루닝 효과 조정 |
| 정렬 키 | sorted_by = ARRAY['col'] | min/max 통계 범위를 좁혀서 프루닝 효과 극대화 |
| 컴팩션 | ALTER TABLE ... EXECUTE optimize | 작은 파일 병합, 정렬 적용 |
| 스냅샷 관리 | EXECUTE expire_snapshots | metadata 크기 감소 |
| 고아 파일 제거 | EXECUTE remove_orphan_files | 불필요한 스토리지 회수 |
12. 성능 저하 진단 체크리스트
문제가 발생했을 때 아래 순서로 확인하세요.
| 순서 | 확인 항목 | 확인 방법 |
|---|---|---|
| 1 | WHERE 절에 함수가 감싸져 있지 않은가 | 쿼리 리뷰 |
| 2 | 파일/파티션 프루닝이 동작하는가 | EXPLAIN (TYPE DISTRIBUTED) |
| 3 | 작은 파일이 과다하지 않은가 | SELECT count(*), sum(file_size_in_bytes) FROM "table$files" |
| 4 | 데이터가 정렬되어 있는가 | sorted_by 속성 확인 |
| 5 | 스냅샷이 과도하게 쌓여 있지 않은가 | SELECT count(*) FROM "table$snapshots" |
| 6 | Dynamic Filtering 이 동작하는가 | EXPLAIN 에서 dynamicFilter 존재 여부 |
| 7 | Worker 메모리가 충분한가 | Trino Web UI → Query Detail → Stage 별 메모리 |
13. 정리
| 항목 | Hive/Impala | Trino + Iceberg |
|---|---|---|
| 파티션 구조 | 디렉터리 기반, HMS 관리 | Manifest 기반, HMS 무관 |
| 파티션 컬럼 누락 시 | 풀스캔 | Hidden partitioning 으로 자동 프루닝 |
| 파티션 수 한계 | 수만~수십만 | 사실상 무제한 |
| 파일 수준 프루닝 | 불가능 | Manifest 의 min/max 통계로 가능 |
| 파티션 전략 변경 | 테이블 재생성 필요 | Partition Evolution 으로 무중단 변경 |
| 동적 파티션 생성 제한 | 있음 (기본 1000) | 없음 |
Hive/Impala 의 파티션 문제는 "사용자의 실수" 가 아니라 디렉터리 기반 파티셔닝의 구조적 한계입니다. Iceberg 는 메타데이터 계층을 테이블 포맷 안으로 가져옴으로써 이 한계를 근본적으로 제거했고, Trino 는 이 메타데이터를 최대한 활용하여 불필요한 I/O 를 줄입니다.
다만 Trino + Iceberg 도 만능은 아닙니다. Small file problem, predicate pushdown 이 깨지는 SQL 패턴, 비정렬 데이터, 스냅샷 누적 등은 여전히 성능을 저하시킵니다. EXPLAIN 으로 실행 계획을 확인하고, 정기적인 컴팩션과 스냅샷 관리를 습관화하는 것이 핵심입니다.
이 글은 Trino 443 + Iceberg spec v2 기준으로 작성되었습니다. 실제 환경에서의 파티션 설계나 마이그레이션에 대해 도움이 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀