Iceberg 是什么
Apache Iceberg 是一种开放表格式:它在对象存储或 HDFS 之上用元数据文件描述「这张表当前由哪些数据文件组成」,把文件组织、分区信息与统计信息集中管理。数据文件本身仍是 Parquet 等通用格式,因此同一张表可以被 Spark、Flink、Trino 等多个引擎同时读写。
快照是怎么工作的
每次写入都会生成一个新的快照与清单文件,指向本次提交后的数据文件集合,而不会就地修改旧文件。这种「先写新文件、再原子提交元数据」的方式让写入具备原子性,也顺带带来了时间旅行与快照回滚能力:查询可以指定某个快照或时间点。
基本信息
| 定位 | 开放表格式,管理元数据与数据文件的关系 |
|---|---|
| 核心机制 | 快照加清单文件描述表状态,写入先提交新快照 |
| 关键能力 | 隐藏分区、时间旅行、schema 演进、增量读 |
| 引擎兼容 | Spark、Flink、Trino、Hive 等可读写同一张表 |
| 日常维护 | 快照过期、清单合并、小文件重写 |
元数据表读法
| 元数据表 | 内容 | 用途 |
|---|---|---|
| table.snapshots | 所有快照及其时间戳 | 定位可回滚的快照编号 |
| table.history | 提交历史与快照演进 | 追查某次变更发生在什么时间 |
| table.files | 当前快照包含的数据文件 | 统计文件数量与大小,判断小文件问题 |
| table.manifests | 清单文件列表 | 分析元数据规模与规划耗时 |
典型使用场景
- 数据湖明细层:需要行级 upsert 与 schema 演进,而 Parquet 目录无法胜任
- 多引擎共用一份数据:批用 Spark、流用 Flink、即席查询用 Trino
- 口径回滚:发现加工逻辑有误时按快照回滚到出问题之前的状态
- 增量读取:只消费两次快照之间新增的数据文件
- 审计与复现:按时间点查询历史状态,复现当时的报表结果
Spark SQL 使用 Iceberg
Spark SQL 使用 Iceberg
-- 按天分区(隐藏分区:查询直接按 ts 过滤即可命中,无需感知分区函数)
CREATE TABLE local.db.orders (
order_id STRING, amount DOUBLE, ts TIMESTAMP
) USING iceberg PARTITIONED BY (days(ts));
INSERT INTO local.db.orders VALUES ('o1', 9.9, TIMESTAMP '2026-10-11 10:00:00');
-- 元数据表:看快照与提交历史
SELECT * FROM local.db.orders.snapshots;
SELECT * FROM local.db.orders.history;
-- 时间旅行:按快照编号或时间点查询历史状态
SELECT * FROM local.db.orders VERSION AS OF 1234567890;
SELECT * FROM local.db.orders TIMESTAMP AS OF '2026-10-11 00:00:00';日常维护过程调用
日常维护过程调用
-- 回滚到某个快照(写错数据后的应急手段)
CALL local.system.rollback_to_snapshot('db.orders', 1234567890);
-- 小文件合并:把大量小文件重写成合适大小的文件
CALL local.system.rewrite_data_files(table => 'db.orders');
-- 快照过期:清理过期快照与孤立文件,避免元数据持续膨胀
CALL local.system.expire_snapshots(table => 'db.orders', older_than => TIMESTAMP '2026-10-01 00:00:00');
CALL local.system.remove_orphan_files(table => 'db.orders');隐藏分区的价值在于把分区细节从查询里去掉:表按 days(ts) 分区,查询直接写 WHERE ts >= ... 就能命中,不必再维护「分区列必须裸用」的书写纪律。
快照与清单不做过期清理,元数据会持续膨胀:表现为查询规划时间越来越长、文件打开开销越来越大,通常需要按保留策略定期执行过期与重写。