Apache Iceberg vs Delta Lake vs Hudi:开放表格式选型对比

对比三大开放表格式在事务模型、引擎兼容、流式更新等方面的差异,按场景给出选型推荐

袋鼠云
袋鼠云
数据治理

为什么需要开放表格式

在对象存储上直接存放 Parquet 或 ORC 文件,你会立即碰到四个无法回避的问题:

  1. 没有原子提交——写入中途崩溃会留下损坏的半成品文件,下游读到脏数据
  2. 无法一致读——读过程中有新文件写入,读到的结果取决于"扫到哪算哪"
  3. Schema 变更无序——加列、删列、改类型只能手动管理,极易出错
  4. 没有时间旅行——想回溯三天前的数据状态,只能靠外部快照或备份

开放表格式(Open Table Format)本质上在文件之上加了一层元数据协议:谁写了什么、什么时候写的、文件之间是什么关系——全部以版本化元数据记录下来。不同引擎读同一张表时遵守同一套协议,数据就真正做到了格式中立、引擎无关。

---

三种架构的核心设计

层级快照树(Snapshot Tree)

一种思路是把每次提交抽象为一棵层级快照树:

metadata.json          ← 指向当前快照
  └─ manifest list     ← 本次快照涉及的 manifest 清单
       ├─ manifest 1   ← 包含若干数据文件的路径、分区范围、列级统计
       ├─ manifest 2
       └─ ...
            ├─ data/file_001.parquet
            ├─ data/file_002.parquet
            └─ ...

这种设计的优势在于:

  • 元数据与数据解耦——新增列只需写新 manifest,不需要重写已有数据文件
  • 列级统计(column stats)存在 manifest 级别,查询下推(partition pruning + column-level filter)极其高效
  • 快照隔离天然支持:读事务永远看到一个一致的快照,不受写入影响

有序事务日志(Transaction Log)

另一种思路是维护一条按序追加的事务日志:

_delta_log/
  ├── 00000000000000000000.json   ← 第一次提交
  ├── 00000000000000000001.json
  ├── 00000000000000000002.json
  ├── 00000000000000000003.checkpoint.parquet  ← 定期压缩为 checkpoint
  └── ...

每次提交追加一个 JSON 文件,记录"添加了哪些文件、删除了哪些文件"。达到阈值后将日志压缩为 Parquet checkpoint,加速恢复。读取时重放日志即可重建表的当前状态。

Timeline + File Group + Record Index

第三种设计面向高频更新场景,引入三个概念:

  • Timeline(时间线):所有操作(commit、clean、compaction)都记录在时间线上,按 instant 排列
  • File Group(文件组):数据文件按逻辑主键分组,同一主键的所有版本落在同一 file group 内
  • Record Index(记录级索引):每条记录映射到一个 file group + offset,写入时直接定位"该更新哪条记录",避免全表扫描

这使得万亿级行的 upsert 成为可能,代价是写入路径比前两种架构更复杂。

---

行级变更的三种策略

Copy-on-Write(COW)

读优化策略:更新时复制整个数据文件,在新文件中写出修改后的行,然后原子替换元数据指针。

  • 优点:读取无额外开销,每个文件都是最新状态
  • 缺点:写放大严重——哪怕只改一行,也要重写整个文件
  • 适用:读多写少的分析型表(如每日报表、维度表)

Merge-on-Read(MOR)

写优化策略:更新只写入一个小的增量日志文件(log file / delta file),读取时将基线文件与增量合并。

  • 优点:写入极快,适合高频更新
  • 缺点:读取需要合并,延迟随未压缩的增量累积而增加;需要定期 compaction
  • 适用:CDC 写入、实时更新流

Deletion Vectors(删除向量)

一种折中方案:更新不复制数据文件,而是在原文件旁写一个紧凑的位图(bitmap),标记哪些行被删除。

  • 优点:写放大极低(接近 MOR),读放大也较低(只需在读取时跳过标记行)
  • 缺点:需要位图与数据文件保持版本对齐;compaction 仍需执行以物理删除
  • 适用:同时追求写性能和读性能的场景;目前主流格式均开始支持

---

并发控制

三种格式都采用乐观并发控制(OCC)——多个写入者可以并行提交,最终只有一个胜出,其余重试。具体协调机制不同:

  • 层级快照树方案:通过 catalog 级别的原子指针交换来协调。写入者提交 manifest 文件后,尝试更新 catalog 指针。如果指针已被其他事务更新,则重试。这使得并发写入的冲突窗口极小
  • 事务日志方案:通过日志的原子追加来保证顺序。两个事务同时追加时,只有先到达的会被接受,后到的需要重试。checkpoint 的生成也通过日志协调
  • Timeline + File Group 方案:早期依赖文件锁,1.0 版本引入了非阻塞并发控制(NBCC)——写入者不需要等待文件级锁,而是通过时间线上的 instant 状态来协调,大幅降低了写入竞争

---

大数据基础平台 EasyMR

统一存储、计算与运维,支撑批、流、OLAP 与湖仓一体的国产化大数据底座平台

了解产品

Schema 演进

这是选型中最容易被忽视却影响深远的维度:

  • 按列 ID 追踪:每次 Schema 变更都分配一个全局递增的列 ID,数据文件中按 ID 存储列值。重命名、删除、添加列都只修改元数据,不需要触碰数据文件。这是最安全、最完整的方案,支持任意顺序的 Schema 变更
  • Column Mapping:在事务日志中维护一个"列名 → 物理位置"的映射表(3.x 版本引入)。效果类似,但实现路径不同
  • Additive/Compatible:仅支持兼容性变更——加新列、用默认值填充;不支持重命名列、删除列等破坏性操作。如果业务需要频繁调整 Schema,这个限制会成为瓶颈

---

分区演进

分区是查询性能的关键杠杆,但传统分区方案一旦确定就无法更改:

  • 真正的分区演进:允许在不重写数据文件的前提下,改变表的分区策略。例如从按天分区改为按月分区,已有的按天分区文件依然有效,查询时自动路由。这是目前唯一支持此能力的方案
  • Liquid Clustering:不使用传统分区,而是在写入时根据聚类键对数据排序。可以随时更改聚类键,但需要重写数据文件来实现最佳布局
  • 表达式索引(Expression Indexes):在数据文件之上建立索引来加速查询,但底层数据的物理布局不变。能力有限,无法替代真正的分区演进

---

流式处理与 CDC

如果你的场景涉及变更数据捕获(CDC)或流式写入,关注点会有所不同:

  • 面向流式设计的方案:record index 可以精确定位每条记录所在的 file group,增量查询只需扫描时间线上的新 instant。内置的 table services(compaction、clustering、cleaning)可以自动在后台运行,减少运维负担。这是为 CDC 场景而生的架构
  • 行级血缘(Row Lineage):在最新的快照方案中引入,可以追溯每一行数据的来源文件和变更历史,对数据治理和审计非常有价值
  • 变更数据馈送(Change Data Feed):在事务日志中显式记录每次提交的 added/removed 行,查询时可以过滤出增量变化

---

数据治理

统一标准、质量、安全与资产管理,沉淀高质量数据集,支撑经营决策、监管合规与 AI 应用。

了解解决方案

生态系统与引擎兼容性

选型时必须考虑你已有的和未来可能使用的计算引擎:

  • 最广的引擎支持:Spark、Flink、Trino/Presto、Snowflake、BigQuery、Athena、Databricks、EMR、Cloudera——几乎所有主流引擎都原生支持
  • 最强的 Spark 集成:与 Spark 深度绑定,性能调优最充分;但对 Flink、Trino 的支持相对滞后
  • 流式场景的偏好:在 Uber、Robinhood 等以实时更新为核心的企业中广泛使用,Spark 和 Flink 均有成熟支持

格式互操作:一种值得关注的趋势是让同一张表可以被不同格式的引擎读取——例如,写入时以事务日志格式存储,但同时生成兼容其他格式的元数据,使得不同引擎无需数据搬迁即可读取同一份数据。

---

选型决策框架

不要纠结于"哪个最好",而是问"我的场景最需要什么":

优先选层级快照树方案的场景:

  • 全新项目,没有历史包袱
  • 需要最广泛的引擎兼容性(多云、多引擎混合部署)
  • 需要真正的分区演进能力
  • 对 Schema 演进的灵活性要求高

优先选事务日志方案的场景:

  • 已深度绑定 Spark 生态,团队熟悉 Spark 调优
  • 读多写少,对读取性能要求极致
  • 已有大量 Parquet 数据文件,迁移成本低

优先选面向流式设计的方案的场景:

  • CDC 写入是核心需求(如用户行为日志、订单状态变更)
  • 需要高频 upsert(每秒数万到数百万次)
  • 需要内置的 table services 自动化运维
  • 对写入延迟敏感,不能接受 COW 的写放大

已有多种格式共存的场景:

  • 考虑使用格式无关的计算引擎,在引擎层屏蔽格式差异
  • 或采用格式互操作方案,让同一份数据同时服务不同格式的读取者

---

落地案例参考

  • 流媒体巨头将全部数据湖迁移到开放表格式,数据规模超过 100 PB,实现了引擎无关的数据访问——同一份数据同时被 Spark、Presto、Flink 读取,不再需要为每个引擎维护独立的数据副本
  • 出行平台利用面向流式的架构处理万亿级行的实时更新,每天处理数十亿条位置和订单记录的 upsert,record index 使查询延迟从分钟级降至秒级
  • 电信运营商通过迁移到事务日志方案,将计算资源消耗降低了 10 倍——得益于更高效的文件布局和查询下推,原本需要大规模集群的查询现在在小规模集群上即可完成

---

总结

开放表格式已经从"要不要用"演进到"该用哪个"。三种架构各有清晰的适用边界:层级快照树胜在灵活性和生态广度,事务日志胜在 Spark 生态内的极致读性能,面向流式的设计胜在高频更新场景的写入效率。关键不是追新,而是根据你的数据规模、写入模式、引擎栈和团队能力做出匹配的选择。一旦选定,投入时间深度优化,远比在格式之间反复摇摆更有价值。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信