Delta Lake 基础:它到底是什么?
说实话,我第一次接触 Delta Lake 的时候,也以为它就是个「加强版 Parquet」。用了一段时间才发现,这玩意儿本质上是一个事务性存储层,跑在云存储或者 HDFS 上面,专门解决数据湖里那些「写了一半不知道怎么办」、「读着读着数据变了」的糟心事。
我习惯这么跟团队讲:Delta Lake 不是数据库,也不是新的文件格式。它就是在 Parquet 文件外面包了一层事务日志,让你能像操作数据库一样操作数据湖。嗯,这个比喻虽然糙了点,但很管用。
一句话总结:Delta Lake = Parquet 文件 + 事务日志(Delta Log)+ ACID 语义。
核心特性之一:ACID 事务
数据湖最头疼的问题是什么?并发写入。两个任务同时往同一个目录写数据,写出来的结果谁都不敢信。Delta Lake 用乐观并发控制解决了这个问题。
我记得有一次,生产环境上 12 个流任务同时往一张 Delta 表写数据,没丢一条,没重一条。换作普通 Hive 表,早就乱成一锅粥了。
| 特性 | 普通数据湖 | Delta Lake |
|---|---|---|
| 原子性 | 写一半失败,留下脏数据 | 要么全写,要么全不写 |
| 一致性 | 读的时候可能看到中间状态 | 读到的永远是完整快照 |
| 隔离性 | 并发写入互相覆盖 | 事务隔离,互不干扰 |
| 持久性 | 依赖底层存储保证 | 日志 + 文件双重保障 |
避坑指南:我曾经遇到一个场景,两个 Spark 作业同时写同一张 Delta 表,其中一个老是报「并发冲突」。后来发现是作业的 spark.sql.shuffle.partitions 设置太大,导致小文件太多,事务日志膨胀。调小分区数之后,问题就解决了。
核心特性之二:时间旅行
时间旅行,说白了就是你能回到过去看数据。Delta Lake 的每个事务都会生成一个版本号,你可以指定版本号或者时间戳来读取历史快照。
这个功能太实用了。我做过一次数据回溯,业务方说三天前的报表数据不对。要是用普通 Hive 表,只能从备份恢复,折腾半天。Delta Lake 呢?一行 SQL 搞定:
-- 按版本号读取
SELECT * FROM my_table VERSION AS OF 123
-- 按时间戳读取
SELECT * FROM my_table TIMESTAMP AS OF '2024-01-15 10:00:00'
你想想看,做数据审计、回滚误操作、对比历史数据,这些场景有了时间旅行就变得特别简单。
注意:时间旅行不是无限保留的。Delta Lake 默认保留 30 天的历史日志,超过这个时间会被 VACUUM 清理掉。如果你需要长期保留历史版本,记得调整 delta.logRetentionDuration 参数。
核心特性之三:Schema 演进
数据湖的 Schema 变化是家常便饭。今天加个字段,明天改个类型,后天删个列。Delta Lake 支持自动 Schema 演进,你不需要手动维护 Hive 元数据。
我个人习惯在写入时开启 mergeSchema 选项:
df.write \
.format("delta") \
.mode("append") \
.option("mergeSchema", "true") \
.save("/path/to/delta-table")
这样新来的数据如果带了新字段,Delta Lake 会自动把 Schema 合并进去。不过要注意,类型变更和字段删除需要显式操作,不能自动处理。
| 操作类型 | 自动演进 | 说明 |
|---|---|---|
| 新增字段 | ✅ 支持 | 自动追加到末尾 |
| 字段类型变更 | ❌ 不支持 | 需要手动 ALTER |
| 字段删除 | ❌ 不支持 | 需要手动 ALTER |
| 字段重命名 | ❌ 不支持 | 需要手动 ALTER |
Delta Lake 与 Apache Spark 的关系
很多人以为 Delta Lake 是 Spark 的一部分,其实不是。Delta Lake 是一个独立的开源项目,只不过它和 Spark 的集成做得特别好。
为什么这么说?因为 Delta Lake 的底层读写引擎完全基于 Spark 的 DataSource API 实现。你用 Spark 读 Delta 表,本质上和读 Parquet、JSON 是一样的,只不过多了一层事务日志的校验。
我举个例子,你在 Spark 里这样写:
spark.read.format("delta").load("/data/events")
spark.write.format("delta").mode("overwrite").save("/data/events")
底层发生了什么?Delta Lake 会先检查事务日志,确认当前最新版本,然后只读取对应版本的文件。写入时,先写数据文件,再提交事务日志。整个过程对用户是透明的。
关键点:Delta Lake 0.6+ 版本开始支持 Scala、Java、Python 和 SQL 四种 API,但底层都是通过 Spark 执行的。没有 Spark,Delta Lake 就跑不起来。
不过,Delta Lake 也在逐步脱离对 Spark 的强依赖。Delta Sharing、Delta Kernel 这些新项目,正在让 Delta Lake 变成一个更通用的存储格式。但就目前而言,Spark 仍然是 Delta Lake 最主流的计算引擎。
本章知识体系总览
下面这张图,把 Delta Lake 的核心概念和它们之间的关系串起来了。我建议你多看两眼,后面章节会反复用到这些概念。
这张图里,从上到下是「概念 → 特性 → 实现」的层次关系。你记住一个核心:Delta Lake 的一切能力,都建立在事务日志和 Parquet 文件之上。后面的实时写入 Kafka 方案,也是在这个基础上搭建的。