2. Lakehouse 核心概念:Delta Lake、Apache Iceberg、Apache Hudi 三大开放表格式对比
聊 Lakehouse,绕不开的就是这三大件:Delta Lake、Iceberg、Hudi。很多人刚接触时容易懵——它们到底解决了什么问题?说白了,传统 Hive 表对 ACID 的支持几乎为零,你跑个批处理写到一半,别人读到的可能是脏数据。我最早在 2018 年做实时数仓时就踩过这个坑,那时候还没这些格式,只能靠分区和重跑补救,累得够呛。
这三兄弟的核心目标其实一致:让数据湖上的表具备数据库一样的事务能力。但实现思路各有侧重。下面我带你逐个拆解。
2.1 它们解决了什么共同问题?
先看一张整体对比图,帮你建立全局认知:
嗯,这张图基本把三者的定位说清楚了。它们都支持 ACID、时间旅行、Schema 演进,但侧重点完全不同。
2.2 Delta Lake:Spark 生态的“亲儿子”
Delta Lake 是 Databricks 开源的,后来捐给了 Linux 基金会。我个人的第一套实时链路就是基于 Delta Lake 搭的,当时看中的就是它跟 Spark 的无缝衔接。
核心机制:事务日志(Transaction Log)
Delta Lake 用 _delta_log 目录下的 JSON 文件记录每次操作。读数据时,引擎会先读日志,再定位到最新版本的文件。这有点像数据库的 WAL(Write-Ahead Log)。
-- 创建 Delta 表
CREATE TABLE events (
event_id BIGINT,
event_time TIMESTAMP,
event_type STRING
) USING DELTA
LOCATION '/data/events';
-- 时间旅行:查询历史版本
SELECT * FROM events VERSION AS OF 5;
-- 或者按时间戳
SELECT * FROM events TIMESTAMP AS OF '2024-01-15 10:00:00';
OPTIMIZE 命令一定要定期跑。我在一个日增 500 万条的项目里,没做 compaction 导致小文件超过 10 万个,查询慢到怀疑人生。跑完 OPTIMIZE ZORDER BY (event_time) 后,性能提升了 3 倍。
适用场景:
- Spark 技术栈为主的公司
- 需要简单易用的 ACID 能力
- 批处理 + 流处理混合场景(Structured Streaming 原生支持)
2.3 Apache Iceberg:开放与多引擎的“瑞士军刀”
Iceberg 由 Netflix 和 Apple 贡献,它的设计哲学是“引擎无关”。我后来在需要同时对接 Flink、Trino、Spark 的项目里,果断换成了 Iceberg——因为它对多引擎的支持最成熟。
核心机制:快照隔离(Snapshot Isolation)
Iceberg 的元数据分三层:Catalog → Metadata File → Manifest List → Manifest File → Data File。每次写操作生成一个新快照,读操作只认快照 ID,读写互不阻塞。
-- 创建 Iceberg 表
CREATE TABLE orders (
order_id BIGINT,
customer_id INT,
amount DECIMAL(10,2),
order_date DATE
) USING ICEBERG
PARTITIONED BY (days(order_date))
LOCATION '/data/orders';
-- 增量读取:只读新增数据
SELECT * FROM orders
FOR SYSTEM_TIME AS OF '2024-02-01 00:00:00'
WHERE order_date >= '2024-02-01';
适用场景:
- 多引擎并存(Spark + Flink + Trino + Presto)
- 需要严格快照隔离的金融、电商场景
- 大规模数据湖(PB 级以上)
2.4 Apache Hudi:增量处理与流式摄取的“急先锋”
Hudi 是 Uber 开源的,最初就是为了解决“如何高效地更新数据湖里的记录”这个问题。如果你要做 CDC(变更数据捕获)入湖,Hudi 几乎是首选。
核心机制:写时复制(COW) vs 读时合并(MOR)
Hudi 提供了两种存储类型:
- Copy on Write (COW):写数据时直接重写整个文件,读性能好,写放大严重。
- Merge on Read (MOR):写数据时只写增量日志(log file),读时合并 base 文件和 log 文件。写性能好,读性能稍差。
-- 创建 Hudi 表(MOR 模式)
CREATE TABLE user_profile (
user_id BIGINT,
name STRING,
email STRING,
last_updated TIMESTAMP
) USING HUDI
TBLPROPERTIES (
'primaryKey' = 'user_id',
'type' = 'mor',
'hoodie.table.name' = 'user_profile'
)
LOCATION '/data/user_profile';
-- 增量查询:只读最近 1 小时的变更
SELECT * FROM user_profile
WHERE `_hoodie_commit_time` > '20240201120000';
hoodie.compact.inline 设成了 false,结果 log 文件越积越多,读查询越来越慢。后来改成每 5 个 commit 自动 compaction 一次,才恢复正常。MOR 模式一定要配好 compaction 策略。
适用场景:
- CDC 入湖(Kafka → Hudi)
- 需要行级更新的场景(如用户画像、订单状态变更)
- 写多读少的实时摄取链路
2.5 三大格式横向对比
下面这张表是我在实际选型时常用的对比维度,你直接拿去用:
| 对比维度 | Delta Lake | Apache Iceberg | Apache Hudi |
|---|---|---|---|
| 创始团队 | Databricks | Netflix / Apple | Uber |
| 事务模型 | 乐观并发控制(OCC) | 快照隔离(Serializable) | 基于时间线的 MVCC |
| 更新方式 | 重写文件(Merge on Read 有限支持) | 重写文件(Copy on Write) | COW + MOR 双模式 |
| 增量查询 | 通过 Change Data Feed | 通过 Incremental Read | 原生增量查询(最强) |
| 引擎支持 | Spark 为主,Flink 有限 | Spark / Flink / Trino / Presto / Hive | Spark / Flink / Presto / Hive |
| Catalog 接口 | Hive Metastore / Unity Catalog | REST / JDBC / Hive / Nessie | Hive Metastore / AWS Glue |
| 小文件合并 | OPTIMIZE 命令 | Rewrite Data Files 动作 | Compaction 机制(自动/手动) |
| 社区活跃度 | 高(Databricks 主导) | 高(中立,厂商参与多) | 中高(Onehouse 等厂商推动) |
2.6 我如何选型?
说实话,没有“最好”的格式,只有“最合适”的。我一般按这个思路判断:
- 如果团队全是 Spark 技术栈,且不想折腾多引擎——选 Delta Lake。开箱即用,文档最全。
- 如果公司有 Flink + Trino + Spark 混用,或者未来可能换引擎——选 Iceberg。它的 REST Catalog 和快照隔离是长期维护的保障。
- 如果核心场景是 CDC 入湖、行级更新、增量 ETL,——选 Hudi。它的 MOR 模式和增量查询能力,其他两家目前还追不上。
我的建议:别在项目初期就纠结“选哪个最好”。先选一个你最熟悉的,跑通一条完整链路。我见过太多团队花两个月对比选型,结果连一条实时流都没跑通。先动起来,再优化。