把按月分区的表改成按天 — 旧文件保持不动
한국어 원문으로 표시합니다.
목표
원천 열(order_ts)에 변환 규칙만 걸어 나누는 숨은 파티셔닝을 만들고, 조건 하나가 몇 개 파일·몇 행을 열게 되는지 계획으로 잰다. 표를 쓰는 도중에 달 단위를 날 단위로 바꾸고(파티션 진화), 옛 파일은 옛 규칙 그대로 둔 채 새 파일만 새 규칙을 따르는 것을 metadata 로 확인한다.
왜 중요한가
Hive 식 표는 파티션을 열로 만든다. order_date 같은 열을 따로 두고, 쓰는 사람도 읽는 사람도 그 열을 알아야 한다. 읽는 쪽이 order_ts 로만 걸면 파티션을 하나도 건너뛰지 못하고, 쓰는 쪽이 시간대를 잘못 계산하면 행이 엉뚱한 파티션에 들어간다. 그리고 파티션 방식을 바꾸려면 표 전체를 다시 써야 한다.
Iceberg 는 파티션을 규칙으로 둔다. days(order_ts) 처럼 원천 열과 변환만 적어 두면 엔진이 파일마다 파티션 값을 계산해 매니페스트에 적고, 읽는 쪽은 order_ts 조건만으로 파일을 건너뛴다. 규칙은 스펙 ID 가 붙어 metadata 에 쌓이므로, 규칙을 바꿔도 옛 파일은 옛 스펙으로 계속 읽힌다. 다시 써야 할 것 같다는 느낌이 가장 흔한 오해다.
단계
- /root/ice/part/month.py(앱
ice-part-month)로lake.part.orders를PARTITIONED BY (months(order_ts))로 만들고 3월 한 달(31개 파일)을 한 번에 커밋하세요. - /root/ice/part/plan1.py(pyiceberg)로
order_ts가 2026-03-10 하루인 조건의 계획을 세워 /root/ice/part/out/plan_march.json 에 쓰세요. - /root/ice/part/evolve.py(앱
ice-part-evolve)로months(order_ts)를days(order_ts)로 바꾸세요. - /root/ice/part/april.py(앱
ice-part-april)로 4월 한 달(30개 파일)을 한 번에 커밋하세요. - /root/ice/part/plan2.py 로 3월 10일과 4월 10일 조건의 계획을 세워 /root/ice/part/out/plan.json 에 쓰세요.
- /root/ice/part/bucket.py(앱
ice-part-bucket)로lake.part.customers를PARTITIONED BY (bucket(4, customer_id))로 만들고 고객을 넣으세요. - /root/ice/part/specs.py 로
lake.part.orders의 기본 스펙 ID 와 스펙별 데이터 파일 수를 /root/ice/part/out/specs.json 에 쓰세요. - /root/ice/part/report.md 에
## 숨은 파티셔닝## 파티션 진화## 버킷세 절을 쓰세요.
참고
- 파티션 값은 디렉터리 이름이 아니라 매니페스트에 있습니다. Spark 에서는
SELECT spec_id, partition, record_count FROM lake.part.orders.files로 봅니다. - pyiceberg 의
tbl.scan(row_filter=…).plan_files()는 파일을 열지 않고 매니페스트(파티션 값·열 통계)만으로 읽을 파일을 고릅니다. order_ts는 시간대가 있는 타임스탬프(timestamptz)입니다. 파이썬 조건에는+00:00이 붙은 ISO 문자열을 쓰세요.- 흔한 실수: 파티션을 바꾼 뒤 3월을
rewrite_data_files로 다시 쓰는 것 — 이 실습에서는 옛 파일을 그대로 두는 것이 정답이고, 다시 쓰면 채점이 떨어집니다. 되돌리려면DROP TABLE lake.part.orders PURGE뒤 1단계부터 하세요. - 공식 문서: Partitioning · Evolution — Partition evolution · Spec — Partition Transforms · Spark DDL — REPLACE PARTITION FIELD
달로 나눈 표 — 파티션 열 없이
/root/ice/part/month.py 를 앱 이름 ice-part-month 로 만들어 lake.part.orders(열 여섯 개, 'format-version' = '2')를 PARTITIONED BY (months(order_ts)) 로 만들고 /data/ice/orders/2026-03-*.csv 31개를 한 번의 append() 로 넣으세요.
months(order_ts) 는 1970년 1월부터 센 달 수를 파티션 값으로 씁니다. 스키마에 새 열이 생기지 않는다는 점을 확인해 보세요. 채점기는 스펙 0 의 변환이 month 인지, 첫 커밋이 3월 전체인지, 파일들의 파티션 값이 모두 2026년 3월인지 봅니다.
하루 조건이 여는 파일 — 달 단위의 한계
/root/ice/part/plan1.py(pyiceberg)로 order_ts >= 2026-03-10T00:00:00+00:00 이고 < 2026-03-11T00:00:00+00:00 인 조건의 스캔을 만들어, 계획된 파일 수·그 파일들의 record_count 합·실제로 걸리는 행 수를 /root/ice/part/out/plan_march.json 에 {"files_planned", "records_scanned", "rows"} 로 쓰세요(지금 표에는 3월만 있습니다).
조건은 order_ts 로만 걸었는데도 파티션 값(달)과 비교되어 다른 달의 파일은 빠집니다. 그런데 3월 10일 하루를 원해도 3월 파일은 한 달 치를 통째로 읽어야 합니다. records_scanned 와 rows 의 차이가 그 비용입니다. 채점기는 첫 스냅샷(3월만 있던 때) 기준으로 같은 계획을 다시 세워 견줍니다.
파티션 진화 — metadata 만 바뀐다
/root/ice/part/evolve.py 를 앱 이름 ice-part-evolve 로 만들어 ALTER TABLE lake.part.orders REPLACE PARTITION FIELD months(order_ts) WITH days(order_ts) 를 돌리세요.
스펙 1 이 새로 생기고 기본(default-spec-id)이 1 로 바뀝니다. 스냅샷은 생기지 않고 데이터 파일도 그대로입니다 — 앞으로 쓰는 파일만 새 규칙을 따릅니다. 채점기는 스펙 목록·기본 스펙·스냅샷 수를 봅니다.
4월 커밋 — 새 파일만 새 스펙
/root/ice/part/april.py 를 앱 이름 ice-part-april 로 만들어 /data/ice/orders/2026-04-*.csv 30개를 한 번의 append() 로 넣으세요. 3월 파일은 다시 쓰지 마세요.
새 커밋의 파일은 spec_id 1 로, 날마다 따로 적힙니다. 3월 파일은 spec_id 0(달) 그대로 같은 스냅샷 안에 함께 삽니다. 읽는 엔진은 스펙마다 따로 계획을 세우므로 두 규칙이 섞여 있어도 결과는 같습니다. 채점기는 3월 파일이 첫 커밋의 경로·스펙 그대로인지도 봅니다.
같은 하루 조건, 다른 비용
/root/ice/part/plan2.py 로 3월 10일 하루와 4월 10일 하루 조건의 계획을 각각 세워 /root/ice/part/out/plan.json 에 {"march": {"files_planned", "records_scanned", "rows"}, "april": {…}} 로 쓰세요.
두 날은 행 수가 비슷한데 읽어야 할 행 수는 크게 다릅니다. 3월은 달 파일을 통째로, 4월은 그날 파일 하나만 엽니다. 채점기는 지금 표로 같은 계획을 다시 세워 견주고, 4월 쪽이 그날 행만 읽는지 봅니다.
가짓수가 많은 열은 버킷으로
/root/ice/part/bucket.py 를 앱 이름 ice-part-bucket 으로 만들어 lake.part.customers(customer_id STRING, tier STRING, region STRING, signup_date DATE) 를 PARTITIONED BY (bucket(4, customer_id)) 로 만들고 /data/ice/customers.csv 를 넣으세요.
bucket(N, 열) 은 값의 32비트 murmur3 해시를 N 으로 나눈 나머지를 파티션 값으로 씁니다. 스펙이 정한 해시라 어느 엔진이 계산해도 같은 칸이 나옵니다 — 채점기는 pyiceberg 로 고객마다 칸을 다시 계산해 파일의 파티션 값과 견줍니다.
한 표 안의 두 규칙
/root/ice/part/specs.py(pyiceberg)로 lake.part.orders 의 기본 스펙 ID 와 스펙별 데이터 파일 수를 /root/ice/part/out/specs.json 에 {"default_spec_id": 정수, "files_by_spec": {"0": 정수, "1": 정수}} 로 쓰세요.
tbl.inspect.files() 의 spec_id 열로 셉니다(content 0 이 데이터 파일). 한 스냅샷 안에 두 스펙의 파일이 섞여 있는 것이 정상입니다.
파티션 설계를 팀 규칙으로
/root/ice/part/report.md 에 ## 숨은 파티셔닝 ## 파티션 진화 ## 버킷 세 절을 쓰세요. 둘째 절에는 5단계의 3월·4월 records_scanned 두 값을 숫자로 넣으세요.
처음에 달로 나눈 선택이 틀렸던 것일까요, 아니면 자료가 늘어 맞지 않게 된 것일까요. 옛 3월 파일을 언젠가 다시 써야 한다면 그 이유는 무엇이어야 하는지도 적어 보세요.