一、引言
随着实时数仓架构从 Lambda 向 Lakehouse 演进,数据湖表格式(Table Format)成为架构选型的核心决策点。Apache Hudi、Apache Iceberg 和 Apache Paimon 是当前最主流的三个开源方案,它们各自从不同的业务场景出发,在 ACID 事务、增量处理、流批一体等维度形成了差异化的技术路线。
本文将从架构设计、核心机制、适用场景三个层面进行深入对比,并结合实时工程实践给出选型建议。
二、Apache Hudi 架构
Hudi 的核心设计围绕Timeline(时间线)和Index(索引)两大机制展开。
┌──────────────────────────────────────────────────────────┐
│ Hudi 表架构 │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Timeline (元数据时间线) │ │
│ │ [commit1] -> [commit2] -> [deltacommit3] -> ...│ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ File Group │ │ File Group │ │ File Group │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │Base File │ │ │ │Base File │ │ │ │Base File │ │ │
│ │ │(Parquet) │ │ │ │(Parquet) │ │ │ │(Parquet) │ │ │
│ │ ├──────────┤ │ │ ├──────────┤ │ │ └──────────┘ │ │
│ │ │Log File 1│ │ │ │Log File 1│ │ │ (COW表) │ │
│ │ │Log File 2│ │ │ └──────────┘ │ │ │ │
│ │ └──────────┘ │ │ (MOR表) │ │ │ │
│ │ (MOR表) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Index (记录到文件的映射) │ │
│ │ Record Key -> File Group ID │ │
│ │ 类型: Bloom / Simple / HBase / Bucket │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
Timeline 机制:每次写入操作(commit/deltacommit/compaction/clean 等)在 Timeline 上记录一个 instant,包含时间戳 + 动作类型 + 状态(requested/inflight/completed),这是 Hudi 实现 ACID 和增量查询的基础。
索引机制:Hudi 通过索引将 Record Key 映射到 File Group,实现高效 Upsert。
- Bloom Filter Index:默认索引,基于文件级别 Bloom Filter,适合大数据量场景。
- Bucket Index:基于 Hash 分桶,确定性路由,Flink 写入推荐使用。
- Record-level Index:基于 HFile 的全局索引,提升 Upsert 效率。
|------|---------------------|---------------------|
| | Copy-on-Write (COW) | Merge-on-Read (MOR) |
| 写入方式 | 每次写入重写整个文件 | 追加写入 Log 文件 |
| 读取性能 | 高(直接读 Parquet) | 需合并 Base + Log |
| 写入延迟 | 较高 | 较低 |
| 适用场景 | 读多写少 | 写多读少、近实时场景 |
三、Apache Iceberg 架构
Iceberg 的核心设计围绕三层元数据树展开,实现了计算引擎与存储的完全解耦。
┌──────────────────────────────────────────────────────────┐
│ Iceberg 元数据架构 │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────┐ │
│ │ Catalog │ (记录当前 metadata 指针) │
│ │ table -> metadata ptr │ │
│ └───────────┬────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Metadata File (.json)│ (Schema/Partition/Snapshot) │
│ │ ┌──────────────────┐ │ │
│ │ │ Snapshot List │ │ │
│ │ │ snap-1 -> mlist-1 │ │ │
│ │ │ snap-2 -> mlist-2 │ │ │
│ │ └──────────────────┘ │ │
│ └───────────┬────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Manifest List (.avro) │ (本次快照包含哪些 manifest) │
│ │ manifest-1.avro │ │
│ │ manifest-2.avro │ │
│ └───────────┬────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Manifest File (.avro) │ (数据文件列表 + 统计信息) │
│ │ data-file-1.parquet │ │
│ │ data-file-2.parquet │ │
│ │ (含 min/max/count │ │
│ │ null_count 等统计) │ │
│ └───────────┬────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Data Files │ (Parquet / ORC / Avro) │
│ └────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
- Snapshot 隔离:每次写操作生成一个新的 Snapshot,读写互不阻塞,支持 Time Travel。
- Hidden Partitioning:分区转换对用户透明,无需在查询中手动指定分区列。例如定义 partition by days(ts)后,查询只需 WHERE ts > '2024-01-01',Iceberg 自动完成分区裁剪。
- Partition Evolution:可在不重写数据的情况下修改分区策略,新数据使用新分区方式,旧数据保持不变。
- Schema Evolution:支持 Add / Drop / Rename / Reorder / Type Promotion,通过列 ID(而非列名)追踪列,保证向前兼容。
四、Apache Paimon 架构
Paimon 的核心设计基于LSM Tree存储结构,原生支持流式写入与 Changelog 生产。
┌──────────────────────────────────────────────────────────────┐
│ Paimon 表架构 │
├──────────────────────────────────────────────────────────────┤
│ │
│ Table │
│ ├── Partition (可选) │
│ │ ├── Bucket 0 │
│ │ │ └── LSM Tree │
│ │ │ ├── Level 0: [sorted-run] [sorted-run] │
│ │ │ ├── Level 1: [sorted-run] │
│ │ │ ├── Level 2: [sorted-run] │
│ │ │ └── ... │
│ │ ├── Bucket 1 │
│ │ │ └── LSM Tree │
│ │ └── ... │
│ └── ... │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Snapshot 管理 │ │
│ │ snapshot-1 -> manifest-list -> manifests -> files│ │
│ │ snapshot-2 -> ... │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Changelog 生产 │ │
│ │ +I / -U / +U / -D (完整 CDC 语义) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
- LSM Tree 存储:数据先写入内存(MemTable),flush 到 Level 0 形成 sorted-run,后台 Compaction 逐层合并。这种结构天然适合高频写入。
- Merge Engine:Primary Key Table 支持多种合并策略:
|----------------|--------------------|--------|
| Merge Engine | 行为 | 适用场景 |
| deduplicate | 保留最新记录 | 去重入湖 |
| partial-update | 多流字段级合并 | 宽表拼接 |
| aggregation | 聚合计算(sum/max/min等) | 预聚合 |
| first-row | 保留首条记录 | 去重保留最早 |
- Changelog 生产:Paimon 可在 Compaction 时生成完整的 changelog(+I/-U/+U/-D),下游 Flink 作业可直接消费,无需依赖 Kafka 作为中间层。
- 流式读取:支持 Streaming Read,Flink 作业可持续消费表的增量变更,实现湖上流处理。
五、核心特性对比
1.特性矩阵
|---------------------|-------------------------------|---------------------------------------------|----------------------------|
| 特性维度 | Hudi | Iceberg | Paimon |
| ACID 事务 | ✅ 基于 Timeline | ✅ 基于 Snapshot | ✅ 基于 Snapshot |
| Upsert 能力 | ✅ 索引驱动,高效 | ✅ Merge-on-Read / COW | ✅ LSM Tree 原生支持 |
| 增量查询 | ✅ 增量 Pull 模型 | ✅ Incremental Scan | ✅ Streaming Read |
| Time Travel | ✅ | ✅ | ✅ |
| Schema Evolution | ✅ | ✅(最完善) | ✅ |
| Partition Evolution | ❌ | ✅(独有优势) | ❌ |
| Hidden Partitioning | ❌ | ✅ | ❌ |
| Changelog 生产 | ⚠️ CDC 格式需额外配置 | ❌ 需外部工具 | ✅ 原生支持 |
| 流式 Sink | ✅ Flink/Spark | ✅ Flink/Spark | ✅ Flink 原生最优 |
| 流式 Source | ⚠️ 支持但非核心路径 | ⚠️ 支持但有限制 | ✅ 原生 Streaming Read |
| 存储结构 | File Group (Base+Log) | 扁平数据文件 | LSM Tree |
| 小文件治理 | Clustering + Compaction | Compaction + Rewrite | Compaction(自动) |
| 引擎兼容性 | Spark/Flink/Hive/Presto/Trino | Spark/Flink/Trino/Presto/Dremio/StarRocks 等 | Flink(最佳)/Spark/Hive/Trino |
2.写入流程对比
┌─────────────── 写入流程对比 ──────────────────────────────┐
│ │
│ 【Hudi COW】 │
│ Record → Index Lookup → 定位 File Group → 读取原文件 │
│ → 合并 → 写入新 Base File → 更新 Timeline │
│ │
│ 【Hudi MOR】 │
│ Record → Index Lookup → 定位 File Group → 追加 Log File │
│ → 更新 Timeline (后台异步 Compaction 合并到 Base File) │
│ │
│ 【Iceberg COW】 │
│ Record → 写入新 Data File → 生成新 Manifest │
│ → 生成新 Manifest List → 生成新 Snapshot → 原子提交 │
│ │
│ 【Iceberg MOR (v2)】 │
│ Delete → 写入 Delete File (position/equality) │
│ → 读取时合并 Data File + Delete File │
│ │
│ 【Paimon Primary Key Table】 │
│ Record → Hash 到 Bucket → 写入 MemTable │
│ → Flush 到 Level 0 → 后台 Compaction 合并各层 │
│ → 生成 Changelog → 更新 Snapshot │
│ │
└───────────────────────────────────────────────────────────┘
3.读取流程对比
|---------|------------------------------|----------------------------|-----------------------------|
| | 快照读 | 增量读 | 流式读 |
| Hudi | 读取最新 commit 的文件(MOR 需 merge) | 指定 beginTime/endTime 范围 | Flink Source 持续拉取 |
| Iceberg | 读取指定 Snapshot 对应的文件 | 对比两个 Snapshot 的差异文件 | Flink Source 监控新 Snapshot |
| Paimon | 读取最新 Snapshot | 读取两个 Snapshot 间的 changelog | Flink Source 持续消费 changelog |
六、场景选型建议
┌────────────────────────────────────────────────────────────────┐
│ 场景匹配推荐 │
├────────────────────────────────────────────────────────────────┤
│ │
│ CDC 增量入湖 (MySQL/PG binlog → 湖) │
│ ├── 推荐: Hudi ★★★ (索引驱动 Upsert, 成熟的 CDC 生态) │
│ ├── 可选: Paimon ★★☆ (LSM 天然适合高频写入) │
│ └── 一般: Iceberg ★★☆ (v2 支持但 Upsert 效率相对偏弱) │
│ │
│ 实时流式数仓 (Flink 流处理链路) │
│ ├── 推荐: Paimon ★★★ (原生 Changelog, 流读写最优) │
│ ├── 可选: Hudi ★★☆ (MOR + Flink 可用) │
│ └── 一般: Iceberg ★★☆ (Flink 集成持续改进中) │
│ │
│ 大规模批处理分析 (多引擎 Ad-hoc 查询) │
│ ├── 推荐: Iceberg ★★★ (引擎无关, 元数据管理强) │
│ ├── 可选: Hudi ★★☆ (COW 表读取性能好) │
│ └── 一般: Paimon ★★☆ (非 Flink 引擎支持在追赶中) │
│ │
│ 多流拼接宽表 (多数据源字段级合并) │
│ ├── 推荐: Paimon ★★★ (partial-update merge engine) │
│ ├── 可选: Hudi ★★☆ (Payload 自定义合并) │
│ └── 一般: Iceberg ★☆☆ (需应用层处理) │
│ │
│ 数据湖上的维表 Join │
│ ├── 推荐: Paimon ★★★ (原生 Lookup Join 优化) │
│ ├── 可选: Hudi ★★☆ (支持但配置复杂) │
│ └── 一般: Iceberg ★★☆ (需额外缓存机制) │
│ │
└────────────────────────────────────────────────────────────────┘