LabHub
시작하기
배우기 러닝패스 코스

레이크하우스 표 형식 — Apache Iceberg 를 metadata 로 이해한다

열을 더하고, 이름을 바꾸고, 넓히고, 지웠다 다시 더한다 — 파일은 한 번도 다시 쓰지 않는다

LabHub 에서 이어서 보기

목표

Iceberg 표의 스키마를 네 가지로 바꾸면서(열 추가·이름 바꾸기·형 넓히기·지웠다 다시 더하기) 옛 데이터 파일이 한 번도 다시 쓰이지 않는데도 올바르게 읽히는 것을 확인한다. 그 비밀이 Parquet 파일 꼬리말에 박힌 field_id 라는 것을 파일을 직접 열어 본다.

왜 중요한가

열을 이름으로 찾는 표(Hive 식 Parquet 표 대부분)에서 이름 바꾸기는 사고다. 옛 파일에는 옛 이름만 있으니 새 이름으로 읽으면 그 열이 통째로 null 이 된다. 열을 위치로 찾는 형식(CSV)에서는 순서를 바꾸는 순간 값이 엉뚱한 열에 들어간다. 그래서 사람들은 스키마를 바꿀 때마다 표 전체를 다시 쓰거나, 아예 바꾸지 못하고 이름이 틀린 열을 몇 년씩 안고 산다. Iceberg 는 열마다 바뀌지 않는 ID 를 주고, 파일을 쓸 때 그 ID 를 함께 적는다. 읽을 때는 이름이 아니라 ID 로 짝을 맞춘다. 그래서 이름 바꾸기는 metadata 한 줄이고, 지운 열과 같은 이름으로 새 열을 더해도 새 ID 라서 옛 값이 되살아나지 않는다. 형은 값이 잘리지 않는 방향(int → long 같은 넓히기)으로만 바꿀 수 있다.

단계

  1. /root/ice/sch/base.py(앱 ice-sch-base)로 lake.sch.orders(format-version 2)를 만들고 2026-03-01 을 넣으세요.
  2. /root/ice/sch/add.py(앱 ice-sch-add)로 coupon STRING 열을 더한 뒤 2026-03-02 를 넣으세요. 이날 행의 couponamount >= 100000 이면 'SPRING', 아니면 null 입니다.
  3. /root/ice/sch/rename.py(앱 ice-sch-rename)로 amountamount_krw 로 바꾸세요.
  4. /root/ice/sch/footer.py(pyarrow·pyiceberg)로 첫 커밋의 데이터 파일 하나를 열어 필드 ID 4 의 파일 속 이름을 /root/ice/sch/out/footer.json 에 쓰세요.
  5. /root/ice/sch/widen.py(앱 ice-sch-widen)로 amount_krwBIGINT 로 넓히고, 다시 INT 로 좁히려 할 때의 오류 조건 이름을 /root/ice/sch/out/narrow.txt 에 쓰세요.
  6. /root/ice/sch/readd.pycoupon 을 지웠다가 같은 이름으로 다시 더하고, 옛 ID·새 ID·지금 null 이 아닌 값의 수를 /root/ice/sch/out/readd.json 에 쓰세요.
  7. /root/ice/sch/history.py 로 스키마 수·현재 스키마 ID·스냅샷 수·살아 있는 데이터 파일 수를 /root/ice/sch/out/history.json 에 쓰세요.
  8. /root/ice/sch/report.md## 이름 바꾸기 ## 형 넓히기 ## 지웠다 다시 더하기 세 절을 쓰세요.

참고

표와 첫 커밋 — 열마다 ID

/root/ice/sch/base.py 를 앱 이름 ice-sch-base 로 만들어 lake.schlake.sch.orders(열 여섯 개, 'format-version' = '2')를 만들고 2026-03-01 파일을 넣으세요.

표를 만들 때 Iceberg 가 열마다 1부터 ID 를 매깁니다(order_id 1 … order_ts 6). 이 ID 는 이름이 바뀌어도 바뀌지 않습니다. 채점기는 metadata 의 스키마에서 ID 와 이름을, 첫 스냅샷에서 행 수를 봅니다.

열 더하기 — 옛 파일은 null 로 읽힌다

/root/ice/sch/add.py 를 앱 이름 ice-sch-add 로 만들어 ALTER TABLE lake.sch.orders ADD COLUMN coupon STRING 을 한 뒤, 2026-03-02 파일에 coupon(amount >= 100000 이면 'SPRING', 아니면 null)을 붙여 넣으세요.

새 열은 새 ID(7)를 받습니다. 3월 1일 파일에는 ID 7 이 없으니 그 행들의 coupon 은 null 로 읽힙니다 — 파일을 다시 쓰지 않았기 때문입니다. 채점기는 3월 2일 커밋이 쓴 Parquet 파일을 직접 열어 ID 7 열의 값이 원본 조건과 맞는지 봅니다.

이름 바꾸기 — metadata 한 줄

/root/ice/sch/rename.py 를 앱 이름 ice-sch-rename 으로 만들어 ALTER TABLE lake.sch.orders RENAME COLUMN amount TO amount_krw 를 돌리세요.

이름 바꾸기는 새 스키마(ID 4 의 이름만 다른)를 metadata 에 더하는 일입니다. 두 날의 파일은 그대로인데도 sum(amount_krw) 가 두 날의 합으로 나옵니다. 채점기는 ID 4 의 이름이 바뀌었는지와 새 이름으로 읽은 합이 원본 합과 같은지 봅니다.

Parquet 꼬리말에 남은 옛 이름

/root/ice/sch/footer.py 로 첫 커밋(3월 1일)의 데이터 파일 하나를 pyarrow.parquet.read_schema 로 열어, PARQUET:field_id 가 4 인 필드의 파일 속 이름을 찾고 /root/ice/sch/out/footer.json{"file", "name_in_file", "field_id", "name_in_table"} 로 쓰세요.

파일은 쓰인 그 순간의 이름(amount)을 그대로 들고 있습니다. 표는 지금 이름(amount_krw)을 씁니다. 둘을 잇는 것이 꼬리말의 field_id 입니다. 첫 커밋의 파일은 pyiceberg 의 tbl.inspect.files(첫_스냅샷_ID) 로 찾을 수 있습니다(경로 앞 file: 은 떼세요).

형은 넓히기만 된다

/root/ice/sch/widen.py 를 앱 이름 ice-sch-widen 으로 만들어 amount_krwBIGINT 로 바꾸고, 이어서 INT 로 되돌리려는 문장을 try 로 감싸 예외의 getCondition()/root/ice/sch/out/narrow.txt 첫 줄에 쓰세요.

int → long 은 어떤 값도 잘리지 않아 옛 파일(int 로 쓰인)을 그대로 long 으로 읽을 수 있습니다. 반대는 값이 잘릴 수 있어 스펙이 허용하지 않습니다. 채점기는 ID 4 의 형이 long 인지와 오류 조건 이름을 봅니다.

지웠다 같은 이름으로 다시 더하면

/root/ice/sch/readd.pycouponDROP COLUMN 한 뒤 같은 이름으로 ADD COLUMN coupon STRING 을 하고, 옛 ID·새 ID·지금 count(coupon)/root/ice/sch/out/readd.json{"old_id", "new_id", "non_null"} 로 쓰세요.

지운 열의 ID 는 다시 쓰이지 않습니다. 새 coupon 은 새 ID 를 받고, 3월 2일 파일에 남아 있는 ID 7 의 'SPRING' 값은 새 열과 짝이 맞지 않아 보이지 않습니다. 이름으로 읽는 표였다면 옛 값이 되살아났을 것입니다. ID 는 pyiceberg 의 tbl.schemas()(이력)와 tbl.schema()(지금)로 읽습니다.

스키마는 다섯, 스냅샷은 둘, 파일은 그대로

/root/ice/sch/history.py(pyiceberg)로 스키마 수·현재 스키마 ID·스냅샷 수·살아 있는 데이터 파일 수를 /root/ice/sch/out/history.json{"schemas", "current_schema_id", "snapshots", "data_files"} 로 쓰세요.

스키마를 바꿀 때마다 metadata 에 스키마가 하나씩 쌓이지만 스냅샷은 늘지 않고, 데이터 파일은 두 커밋이 쓴 그대로입니다. 채점기는 네 값을 metadata 와 견주고, 지금 살아 있는 파일이 두 번째 스냅샷의 파일과 정확히 같은지(다시 쓰인 파일이 없는지)도 봅니다.

스키마 변경 규칙을 팀 규칙으로

/root/ice/sch/report.md## 이름 바꾸기 ## 형 넓히기 ## 지웠다 다시 더하기 세 절을 쓰세요. 셋째 절에는 6단계의 옛 ID 와 새 ID 를 숫자로 넣으세요.

이 표를 여러 팀이 읽는다면 어떤 변경은 언제든 해도 되고 어떤 변경은 알려야 하는지 적어 보세요. 필드 ID 로 읽지 않는 소비자(파일을 직접 여는 스크립트)가 있다면 무엇이 깨지는지도.