1. Delta Lake 前世今生:从数据湖痛点出发
聊 Delta Lake 之前,我想先聊聊数据湖。2015 年左右,我接手了一个企业级数据平台项目。客户说他们要把所有数据都扔到 HDFS 上,用 Spark 随便查。听起来很美好,对吧?结果呢?三个月后,数据湖变成了「数据沼泽」。
数据湖的核心理念其实很吸引人——把所有原始数据原封不动存下来,等需要的时候再处理。但理想很丰满,现实很骨感。我在那个项目里踩过的坑,几乎涵盖了数据湖的所有典型问题。
数据湖的四大痛点
先说说我亲身经历的几个场景。你想想看,一个 PB 级的数据湖,每天有几百个任务在读写。突然有一天,某个 ETL 任务写到一半挂了。结果呢?数据文件里混了一半旧数据、一半新数据。下游报表跑出来,数字对不上。业务部门炸了锅。
这就是数据湖的第一个痛点——缺乏事务支持。没有 ACID,数据一致性全靠运气。
核心问题:传统数据湖不支持原子性写入。任务失败后,部分写入的数据无法自动回滚,导致数据处于「半成品」状态。
第二个痛点,文件元数据膨胀。我记得有个项目,Hive 表的分区数超过 10 万。每次执行 SHOW PARTITIONS 都要等几分钟。NameNode 的内存被元数据撑爆,集群频繁告警。为什么会这样?因为 HDFS 的 NameNode 把所有文件元数据都放在内存里。文件一多,内存就扛不住了。
第三个痛点,数据质量难以保证。我遇到过最离谱的情况——某个上游系统改了字段类型,从 int 变成了 string。下游的 Spark 任务直接报错,整个管道停了 6 个小时。没有 Schema 强制约束,数据湖里的数据就像脱缰的野马。
第四个痛点,批流割裂。批处理用 Hive,流处理用 Kafka + Flink。两套系统,两套代码,两套运维。数据对不上,时间窗口对不上,业务逻辑对不上。说白了,就是批和流之间隔着一道墙。
| 痛点 | 具体表现 | 影响 |
|---|---|---|
| 缺乏事务 | 写入失败后数据不完整 | 数据一致性差,报表不可信 |
| 元数据膨胀 | 文件数过多,NameNode 内存不足 | 集群性能下降,运维成本高 |
| 数据质量差 | Schema 变更无约束 | 管道频繁中断,修复成本高 |
| 批流割裂 | 两套系统,两套代码 | 数据口径不一致,维护困难 |
Delta Lake 的诞生
2019 年,Databricks 开源了 Delta Lake。说实话,我第一次看到它的设计文档时,心里想的是——这不就是给数据湖加了个事务日志吗?但真正用起来才发现,这个「事务日志」解决了上面所有痛点。
Delta Lake 的核心思路很简单:在数据文件之上,增加一层事务日志(Transaction Log)。每次读写操作,都会先写日志,再操作文件。日志里记录了每个文件的版本、状态、Schema 信息。这样一来,ACID 事务、Schema 强制、时间旅行(Time Travel)这些能力就都有了。
个人经验:我建议你在评估 Delta Lake 时,先关注它的「事务日志」设计。这是 Delta Lake 区别于其他数据湖方案的根本所在。理解了事务日志,就理解了 Delta Lake 的一半。
核心价值:从「湖」到「仓」的桥梁
Delta Lake 不是要取代数据湖,而是给数据湖加上「仓库」的能力。我把它总结为三个核心价值:
- ACID 事务——支持原子性、一致性、隔离性、持久性。写入失败自动回滚,不会留下脏数据。
- Schema 演进——支持自动合并 Schema 变更。字段新增、类型修改都能自动处理,不会因为上游变更导致管道中断。
- 时间旅行——可以查询任意历史版本的数据。数据回滚、审计追溯变得非常简单。
嗯,这里要注意。Delta Lake 的时间旅行不是无限保留所有版本。它默认保留 30 天的历史数据。你可以通过 spark.databricks.delta.retentionDurationCheck.enabled 配置调整。我曾经有个项目需要保留 90 天的历史版本做审计,调大这个参数后,存储成本涨了 30%。所以,时间旅行虽好,也要算好存储账。
避坑指南:我曾经在生产环境遇到过一个问题——Delta Lake 表的 VACUUM 操作默认清理 7 天前的文件。如果某个流任务刚好在 7 天前停止过,清理后就没法回溯了。建议你在配置 VACUUM 时,先确认所有下游任务的时间窗口。
Delta Lake 架构概览
下面这张图展示了 Delta Lake 的核心架构。你可以看到,事务日志(_delta_log)是 Delta Lake 的大脑,所有读写操作都通过它来协调。
从架构图可以看出,Delta Lake 在数据源和查询引擎之间加了一层事务日志。这层日志不仅记录了每次写入的元数据,还维护了文件的版本链。查询引擎读取数据时,先查事务日志,找到当前版本对应的文件列表,再读取实际数据。这样一来,读写分离,互不干扰。
为什么是 Delta Lake?
市面上类似的方案还有 Apache Iceberg 和 Apache Hudi。我三个都用过,简单说说我的感受。
Delta Lake 的优势在于:与 Spark 深度集成。如果你团队的技术栈以 Spark 为主,Delta Lake 的学习成本几乎为零。它的 API 设计完全遵循 Spark 的习惯,从 DataFrame 读写到 SQL 操作,无缝切换。
Iceberg 的优势在于:查询引擎兼容性更好。它原生支持 Flink、Trino、Spark 等多种引擎。如果你的团队用 Flink 做流处理,Iceberg 可能更合适。
Hudi 的优势在于:增量处理能力更强。它支持 Upsert、Delete 等变更操作,适合 CDC 场景。
我个人习惯这样选型:
- Spark 为主 + 需要 ACID → Delta Lake
- 多引擎混合 + 需要 Schema 演进 → Iceberg
- CDC 场景 + 需要增量处理 → Hudi
核心观点:Delta Lake 不是银弹。它最适合的场景是「以 Spark 为核心的数据湖 + 数据仓库混合架构」。如果你的团队已经用 Spark 做 ETL 和数据分析,Delta Lake 是最自然的选择。