๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด๋ง

์‹ค์‹œ๊ฐ„ ๋ถ„์„ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• - 02. Iceberg ์ฃผ์š” ๊ธฐ๋Šฅ ์†Œ๊ฐœ

Tempo 2025. 5. 19. 08:40
Iceberg์˜ ์ฃผ์š” ๊ธฐ๋Šฅ์„ ์†Œ๊ฐœํ•˜๊ณ  Delta-lake, Hudi์™€ ๋น„๊ตํ•˜์—ฌ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.
์ด์ „์— ์„ค์น˜ํ•œ Docker Compose์—๋Š” Iceberg์˜ ์˜ˆ์‹œ ์‹คํ–‰ Jupyter ๋…ธํŠธ๋ถ๋„ ํ•จ๊ป˜ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํŒŒ์ผ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ค์Šตํ•˜๋ฉฐ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.(https://jongwho.tistory.com/37)
 

Spark ์‹คํ–‰ ์‹œ์ž‘

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("Jupyter").getOrCreate()

spark
-- ํ…Œ์ด๋ธ” ์ƒ์„ฑ
CREATE DATABASE IF NOT EXISTS nyc
 

๋ฐ์ดํ„ฐ ๋กœ๋“œ ๋ฐ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

df = spark.read.parquet("/home/iceberg/data/yellow_tripdata_2021-04.parquet")
df.write.saveAsTable("nyc.taxis")

PySpark๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ํ™•์ธ

df.show(n=10, truncate=False, vertical=True)

์ปฌ๋Ÿผ ๋ฐ ๊ฐ’ ์˜ˆ์‹œ

 

 

๐Ÿ”‘ Iceberg์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

1. โœ… ์Šคํ‚ค๋งˆ ์ง„ํ™”(Schema Evolution)

Iceberg๋Š” ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ํ…Œ์ด๋ธ”์˜ ์Šคํ‚ค๋งˆ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ปฌ๋Ÿผ ์ถ”๊ฐ€, ์ด๋ฆ„ ๋ณ€๊ฒฝ, ์ˆœ์„œ ๋ณ€๊ฒฝ์ด ๋ชจ๋‘ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ€๋Šฅ
  • ๋‚ด๋ถ€์ ์œผ๋กœ field ID๋ฅผ ์‚ฌ์šฉํ•ด, ์ˆœ์„œ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ธ์‹

๐Ÿ“Œ ์˜ˆ์‹œ:

-- ์ปฌ๋Ÿผ ๋ณ€๊ฒฝ
ALTER TABLE nyc.taxis RENAME COLUMN fare_amount TO fare
ALTER TABLE nyc.taxis RENAME COLUMN trip_distance TO distance

-- ์ฝ”๋ฉ˜ํŠธ ์ถ”๊ฐ€
ALTER TABLE nyc.taxis ALTER COLUMN distance COMMENT 'The elapsed trip distance in miles reported by the taximeter.'

-- ํƒ€์ž… ๋ณ€๊ฒฝ
ALTER TABLE nyc.taxis ALTER COLUMN distance TYPE double;

-- ์ปฌ๋Ÿผ์˜ ์ˆœ์„œ ๋ณ€๊ฒฝ: ํ…Œ์ด๋ธ” ์กฐํšŒ ์‹œ ํ‘œ์‹œ๋˜๋Š” ์ปฌ๋Ÿผ์˜ ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. distance ๋’ค์— fare ์ปฌ๋Ÿผ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
ALTER TABLE nyc.taxis ALTER COLUMN distance AFTER fare;

-- ์ปฌ๋Ÿผ ์ถ”๊ฐ€: ๊ธฐ์กด HDFS์™€ ๋‹ค๋ฅด๊ฒŒ ์ปฌ๋Ÿผ์„ ์ถ”๊ฐ€ํ•ด๋„ nulใ…ฃ ๊ฐ’์œผ๋กœ ์šฐ์„  ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
ALTER TABLE nyc.taxis
ADD COLUMN fare_per_distance_unit float AFTER distance

-- ์ถ”๊ฐ€ํ•œ ์ปฌ๋Ÿผ์— ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
UPDATE nyc.taxis
SET fare_per_distance_unit = fare/distance
 
 

๐Ÿง  ์™œ ์ค‘์š”ํ•œ๊ฐ€์š”?
๊ธฐ์กด Hive ํ…Œ์ด๋ธ”์€ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์‹œ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ๋ถˆ์•ˆ์ •ํ•œ ๋™์ž‘์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์ง€๋งŒ,
Iceberg๋Š” ๋ช…์‹œ์  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ๋กœ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ์œ ์ง€ํ•˜๋ฉฐ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

2. ๐Ÿงญ Time Travel

Iceberg๋Š” ์ด์ „ ์‹œ์ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ์Šค๋ƒ…์ƒท ๋‹จ์œ„๋กœ ๊ธฐ๋ก๋˜๋ฉฐ, ํŠน์ • snapshot ID ๋˜๋Š” timestamp๋กœ ์ด์ „ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ์˜ˆ์‹œ:

-- ํ…Œ์ด๋ธ”์˜ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ
SELECT * FROM nyc.taxis.history
 
fare_per_distance_unit ์ปฌ๋Ÿผ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šค๋ƒ…์ƒท์ด ์ƒ๊ธด๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 
 
original_snapshot = df.head().snapshot_id
spark.sql(f"CALL system.rollback_to_snapshot('nyc.taxis', {original_snapshot})")
original_snapshot

์›๋ณธ Snapshot id(259~)๋กœ ์›๋ณตํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด fare_per_distance_unit ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐Ÿ” Iceberg Snapshot์ด ์ƒ์„ฑ๋˜๋Š” ์กฐ๊ฑด์€?

  • ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ(์‚ฝ์ž…, ์ˆ˜์ •, ์‚ญ์ œ)์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ƒ…์ƒท์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, ํ…Œ์ด๋ธ”์— ์ƒˆ๋กœ์šด ํ–‰์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ • ๋˜๋Š” ์‚ญ์ œํ•˜๋Š” DML ์ž‘์—…์ด ์‹คํ–‰๋˜๋ฉด Iceberg๋Š” ์ž๋™์œผ๋กœ ์ƒˆ๋กœ์šด ์Šค๋ƒ…์ƒท์„ ๋งŒ๋“ค์–ด ํ•ด๋‹น ์‹œ์ ์˜ ํ…Œ์ด๋ธ” ์ƒํƒœ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
  • ์Šค๋ƒ…์ƒท์€ Iceberg์˜ commit ์ž‘์—…์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    ๋ฐ์ดํ„ฐ๊ฐ€ ํ…Œ์ด๋ธ”์— ๋ฐ˜์˜๋˜๋Š” commit ์‹œ์ ๋งˆ๋‹ค ํ…Œ์ด๋ธ”์˜ ์ „์ฒด ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์Šค๋ƒ…์ƒท์ด ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ์ด commit์—๋Š” ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…๋ฟ ์•„๋‹ˆ๋ผ, overwrite, delete, update ๋“ฑ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ž‘์—…์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • ์Šค๋ƒ…์ƒท์€ ํ…Œ์ด๋ธ” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ(์˜ˆ: ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ, ํŒŒํ‹ฐ์…˜ ๋ณ€๊ฒฝ)์—๋„ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์ด๋‚˜ ํŒŒํ‹ฐ์…˜ ๊ตฌ์กฐ ๋ณ€๊ฒฝ ๋“ฑ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์ž‘์—…๋„ ์ƒˆ๋กœ์šด ์Šค๋ƒ…์ƒท์„ ์ƒ์„ฑํ•˜๋Š” ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค(์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ commit์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ).

๐Ÿง  ํ™œ์šฉ ์˜ˆ์‹œ

  • ์ž˜๋ชป๋œ ์—…๋ฐ์ดํŠธ ๋ณต๊ตฌ
  • ํžˆ์Šคํ† ๋ฆฌ ๋น„๊ต
  • ์‹œ์  ๊ธฐ๋ฐ˜ ๋ถ„์„ (๊ณผ๊ฑฐ ์ƒํƒœ ๋ถ„์„)

3. ๐Ÿ“‚ ์ž๋™ ํŒŒํ‹ฐ์…”๋‹ (Hidden Partitioning)

Iceberg๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ํŒŒํ‹ฐ์…”๋‹ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…˜์„ ํ…Œ์ด๋ธ” ์ •์˜์—๋งŒ ์„ ์–ธํ•˜๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ ์ž๋™์œผ๋กœ ํŒŒํ‹ฐ์…˜ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

๐Ÿ“Œ ์˜ˆ์‹œ:

ALTER TABLE nyc.taxis
ADD PARTITION FIELD VendorID
 

๐Ÿง  ์žฅ์ 

  • ์‚ฌ์šฉ์ž๋Š” ํŒŒํ‹ฐ์…”๋‹์„ ์˜์‹ํ•˜์ง€ ์•Š๊ณ  SQL๋งŒ์œผ๋กœ ์ž‘์—… ๊ฐ€๋Šฅ
  • ๋ณต์žกํ•œ ๋””๋ ‰ํ† ๋ฆฌ ์„ค๊ณ„ ์—†์ด๋„ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ฐ€๋Šฅ
  • ํŒŒํ‹ฐ์…˜ ์ปฌ๋Ÿผ์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ์ด์ „ ํŒŒํ‹ฐ์…˜์ด ๋ฌดํšจํ™”๋˜์ง€ ์•Š์Œ

4. ๐Ÿ”„ ACID ํŠธ๋žœ์žญ์…˜ ์™„๋ฒฝ ์ง€์›

Iceberg๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์กฐ์ž‘์„ ์Šค๋ƒ…์ƒท ๊ธฐ๋ฐ˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๋™์‹œ์— ์ด๋ฃจ์–ด์ ธ๋„ ์ถฉ๋Œ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ๋ณ‘ํ•ฉ๋ฉ๋‹ˆ๋‹ค.

๐Ÿง  ์‹ค๋ฌด์—์„œ ์ค‘์š”ํ•œ ์ด์œ 

  • ๋ฐฐ์น˜ & ์ŠคํŠธ๋ฆฌ๋ฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋™์‹œ์— ์šด์˜ํ•  ๋•Œ ์ถฉ๋Œ ์—†์Œ
  • Spark, Flink, Trino ๋“ฑ ๋‹ค์–‘ํ•œ ์—”์ง„์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณ‘๋ ฌ ์ž‘์—… ๊ฐ€๋Šฅ

5. ๐Ÿงฑ ์Šค๋ƒ…์ƒท(Snapshot) ๊ธฐ๋ฐ˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

Iceberg๋Š” ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚  ๋•Œ๋งˆ๋‹ค ์Šค๋ƒ…์ƒท์„ ์ƒ์„ฑํ•˜๊ณ , ๋ชจ๋“  ํ…Œ์ด๋ธ”์˜ ์ƒํƒœ๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

SELECT snapshot_id, manifest_list
FROM nyc.taxis.snapshots

minio์— ์กด์žฌํ•˜๋Š” snap-xx ๋ฐ์ดํ„ฐ

๐Ÿ’ก ์Šค๋ƒ…์ƒท ๊ตฌ์„ฑ ์š”์†Œ:

  • metadata.json: ํ…Œ์ด๋ธ” ๊ตฌ์กฐ, ์Šค๋ƒ…์ƒท ๋ชฉ๋ก
  • manifest list: ์–ด๋–ค manifest ํŒŒ์ผ์ด ํฌํ•จ๋˜์—ˆ๋Š”์ง€
  • manifest file: ์–ด๋–ค ๋ฐ์ดํ„ฐ ํŒŒ์ผ์ด ์–ด๋–ค ํŒŒํ‹ฐ์…˜์— ์žˆ๋Š”์ง€

๐Ÿง  ์™œ ์ด๊ฒŒ ์ค‘์š”ํ• ๊นŒ?

  • ํ…Œ์ด๋ธ”์„ ์žฌ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฒ„์ €๋‹๊ณผ ๋กค๋ฐฑ ๊ฐ€๋Šฅ
  • ํ…Œ์ด๋ธ” ์ƒํƒœ๋ฅผ ๊ฒฝ๋Ÿ‰ํ™”๋œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ๋งŒ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
  • ๊ฐ์ฒด ์Šคํ† ๋ฆฌ์ง€ ํ™˜๊ฒฝ(S3, GCS ๋“ฑ)์— ์ตœ์ ํ™”

6. ๐Ÿš€ ๋ฉ€ํ‹ฐ ์—”์ง„ ํ˜ธํ™˜์„ฑ

Iceberg๋Š” ๋‹ค์–‘ํ•œ ์—”์ง„์—์„œ ๋™์ผํ•œ ํ…Œ์ด๋ธ” ํฌ๋งท์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • Spark
  • Trino / Presto
  • Flink
  • Hive (์ผ๋ถ€ ์ œํ•œ์ )

๐Ÿง  ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์„ ์—ฌ๋Ÿฌ ์—”์ง„์—์„œ ๋ณ‘๋ ฌ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์€ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ›จ์”ฌ ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

7. ๐Ÿ“‰ ์ฟผ๋ฆฌ ์ตœ์ ํ™” ๊ธฐ๋Šฅ: Pruning & Pushdown

Iceberg๋Š” ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

  • Column Pruning: ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์ฝ์Œ
  • Partition Pruning: ์กฐ๊ฑด์— ๋”ฐ๋ผ ํŒŒํ‹ฐ์…˜ ์Šค์บ” ์ตœ์†Œํ™”
  • Predicate Pushdown: ์ฟผ๋ฆฌ ์กฐ๊ฑด์„ ์Šคํ† ๋ฆฌ์ง€ ๋ ˆ๋ฒจ๋กœ ์ „๋‹ฌ

์ด๋Ÿฌํ•œ ์ตœ์ ํ™”๋Š” ๋‚ด๋ถ€์˜ ํŒŒ์ผ ์ˆ˜์ค€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋•๋ถ„์— ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ” Iceberg vs Delta Lake vs Hudi ๋น„๊ต

๊ธฐ๋ŠฅIcebergDelta LakeHudi

 

  Iceberg Delta-Lake Hudi
ACID ํŠธ๋žœ์žญ์…˜ โœ… โœ… โœ…
Time Travel โœ… โœ… ์ œํ•œ์ 
Schema Evolution โœ… (์•ˆ์ •์ ) โœ… โœ…
ํŒŒํ‹ฐ์…”๋‹ ์ถ”์ƒํ™” โœ… (์ž๋™ ํŒŒํ‹ฐ์…”๋‹) โŒ โŒ
๋ฉ€ํ‹ฐ์—”์ง„ ์ง€์› โœ… (Spark, Trino, Flink ๋“ฑ) Spark ์ค‘์‹ฌ Flink ์ค‘์‹ฌ
์Šค๋ƒ…์ƒท ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ โœ… (๋…๋ฆฝ์  ๊ตฌ์กฐ) ์ œํ•œ์  ์ œํ•œ์ 
์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ์ ํ•ฉ์„ฑ โœ… โŒ โœ…
๋ฐ˜์‘ํ˜•