构建 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。
- 以过短时间窗口移除孤立文件 — 可能删除正在写入的写操作所需的文件;请使用安全的截止时间。