LabHub
开始
学习 学习路径 课程

湖仓表格式 — 从元数据理解 Apache Iceberg

分区是规则而不是列 — 隐藏分区与分区演进

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

한 줄 요약

Iceberg 는 파티션 값을 사람이 만든 열이 아니라 원천 열에 건 변환 규칙(예: days(order_ts))으로 계산해 매니페스트에 적고, 읽는 쪽은 원천 열 조건만으로 파일을 건너뛴다. 규칙은 스펙 ID 를 달고 쌓이므로 도중에 바꿔도 옛 파일은 옛 규칙 그대로 읽힌다.

왜 Hive 식 파티션이 문제였나

Partitioning 문서가 드는 예가 전형적이다. 로그 표를 날짜로 나누려면 Hive 에서는 event_date 라는 을 따로 만들고, 쓰는 쪽이 event_time 에서 그 값을 계산해 넣어야 한다. 여기서 문제가 줄줄이 생긴다.

어떻게 동작하나 — 변환과 파티션 스펙

Iceberg 의 파티션 스펙은 필드마다 원천 열 ID, 파티션 필드 ID, 변환, 이름을 가진다. 엔진은 행을 쓸 때 변환을 계산해 그 파일의 파티션 값으로 매니페스트에 적는다. 한 파일의 행은 모두 같은 파티션 값을 가진다. 변환 목록은 다음과 같다.

변환 결과 쓰임
identity 값 그대로 가짓수가 적은 범주 열
year · month · day · hour 1970 년부터 센 해·달·날·시간 시각 열
bucket[N] 해시를 N 으로 나눈 나머지 가짓수가 많은 키(고객 번호)
truncate[W] 폭 W 로 자른 값 숫자 구간, 문자열 앞부분
void 늘 null 판 1 에서 필드를 없앨 때

bucket은 32비트 Murmur3(x86, 시드 0) 해시의 부호 비트를 버리고 N 으로 나눈다. 스펙이 해시를 정해 두었기 때문에 Spark 가 쓰고 파이썬이 읽어도 같은 값이 같은 칸에 간다.

읽는 쪽은 order_ts >= X 같은 원천 열 조건만 쓴다. 스캔 계획이 그 조건을 파티션 조건(order_ts_day >= day(X))으로 바꿔 매니페스트의 파티션 값과 견준다. 이 변환은 '포함하는' 쪽으로 계산되므로 조건에 맞는 행이 있을 수 있는 파일은 절대 빠지지 않는다. 사용자는 파티션이 어떻게 나뉘었는지 몰라도 된다 — 그래서 '숨은' 파티셔닝이다.

파티션 진화 — 옛 파일은 그대로

자료가 늘어 달 단위가 너무 굵어졌다고 하자. Evolution 문서에 따르면 스펙을 바꿔도 옛 자료는 옛 스펙 그대로 남고 새 자료만 새 배치로 쓰인다. 스펙은 목록에 쌓이고 매니페스트마다 자기가 쓴 스펙 ID 를 기억한다. 계획할 때는 스펙마다 따로 조건을 바꿔 파일을 고르는데, 문서는 이것을 split planning 이라 부른다. 그리고 파티션 진화는 metadata 작업이라 파일을 서둘러 다시 쓰지 않는다고 분명히 적는다.

Spark 에서는 ALTER TABLE 로 필드를 더하고(ADD PARTITION FIELD), 빼고(DROP PARTITION FIELD), 바꾼다(REPLACE PARTITION FIELD … WITH …).

ALTER TABLE lake.demo.events REPLACE PARTITION FIELD months(event_ts) WITH days(event_ts);

현장에서 만나는 모습

'파티션을 바꿨으니 옛 자료도 다시 써야 한다'. 가장 흔한 오해다. 옛 달 단위 파일은 그대로 두어도 정확히 읽힌다. 다시 쓰는 것은 선택이고, 이유가 있어야 한다 — 옛 기간을 하루 단위로 자주 조회해서 읽는 양을 줄이고 싶을 때처럼. 그때도 압축(rewrite_data_files) 같은 별도 작업으로 한다.

하루 조건인데 한 달 치를 읽는다. 달 단위 파일에 하루 조건을 걸면 파티션으로는 그 달 파일을 통째로 골라야 한다. 파일 안의 열 통계(하한·상한)가 도움이 되지만 파일이 한 달을 고루 담고 있으면 소용없다. 계획이 여는 파일의 행 수와 실제로 걸리는 행 수의 차이가 그 비용이다.

고객 번호로 나눴더니 파티션이 수만 개. identity 로 가짓수가 많은 열을 나누면 작은 파일이 폭발한다. bucket 으로 칸 수를 정해 두는 것이 맞다.

실무에서 진짜 중요한 것

다음 실습에서 할 것

3월 한 달을 months(order_ts) 로 나눈 표에 넣고 pyiceberg 로 하루 조건의 계획을 세워 몇 파일·몇 행을 여는지 잰다. 파티션을 days(order_ts) 로 바꾼 뒤 4월을 넣고, 3월 파일은 스펙 0 그대로·4월 파일만 스펙 1 인 것을 확인한다. 3월 하루와 4월 하루 조건이 읽는 행 수를 견주고, 고객 표를 bucket(4, customer_id) 로 나눠 칸이 스펙대로 계산됐는지 본다.