建立 Iceberg 資料表
使用時機
- 在湖倉上建立或維護 Apache Iceberg 資料表。
- 選擇分割方式,或演進分割/綱要而無需重寫資料。
- 管理快照、時間旅行、壓縮與小檔案。
- 不要用於 Delta 專屬工作(請使用
engineering-databricks-pipelines)。
工作流程
- [ ] 根據查詢篩選欄位進行分割;使用隱藏分割轉換
- [ ] 使用 MERGE 進行冪等 upsert
- [ ] 排程壓縮(rewrite_data_files)以修復小檔案
- [ ] 過期舊快照並移除孤兒檔案,以控制中繼資料/儲存空間
- [ ] 透過欄位 ID 演進分割/綱要(無需資料重寫)
- 根據篩選欄位進行分割,使用隱藏分割轉換
(
days(ts)、bucket(N, id)),讓查詢能自動進行分割剪枝,無需使用者手動加入衍生分割欄位。 - 冪等寫入,透過以商業鍵為條件的
MERGE INTO。 - 定期壓縮——串流/小批次寫入會產生大量小檔案;
rewrite_data_files可恢復讀取效能。 - 維護中繼資料——過期舊快照並移除孤兒檔案,否則快照歷史與儲存空間會無限增長。
- 自由演進——Iceberg 透過 ID 追蹤欄位/分割,因此新增/刪除/重新命名,甚至分割規格變更都無需資料重寫。
模式
建立時使用隱藏分割 + MERGE upsert:
CREATE TABLE lake.db.orders (order_id BIGINT, customer_id BIGINT, amount DECIMAL, ordered_at TIMESTAMP)
USING iceberg PARTITIONED BY (days(ordered_at));
MERGE INTO lake.db.orders t USING staging s ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;
維護(Spark 程序):
CALL lake.system.rewrite_data_files('db.orders'); -- 壓縮小檔案
CALL lake.system.expire_snapshots('db.orders', TIMESTAMP '2026-08-01 00:00:00');
CALL lake.system.remove_orphan_files(table => 'db.orders');
時間旅行——讀取先前的快照以進行稽核或復原:
SELECT * FROM lake.db.orders VERSION AS OF <snapshot_id>。
分割演進——ALTER TABLE ... ADD PARTITION FIELD bucket(16, customer_id)
僅套用於新資料;舊資料仍然有效。
常見陷阱
- 串流資料表未進行壓縮——小檔案爆炸會嚴重拖慢讀取速度。
- 從未過期快照——中繼資料和儲存空間無限制增長;應在時間旅行保留期限內設定過期。
- 過度分割(例如對低資料量按小時分割)——產生過多微小分割區;應根據資料量調整粒度。
- 加入明確的衍生分割欄位——違背隱藏分割的本意;請使用轉換函式。
- 直接覆蓋而非 MERGE——失去冪等性;請按鍵值使用 MERGE。
- 移除孤兒檔案時時間窗口過短——可能刪除仍在寫入中的檔案;請使用安全截止時間。