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 核心知识体系 Delta Lake ACID 事务 时间旅行 Schema 演进 事务日志 (Delta Log) Parquet 文件 Spark 引擎 核心关系 Delta Lake 在 Parquet 文件之上,通过事务日志提供 ACID 语义 所有读写操作都依赖 Spark 引擎执行

这张图里,从上到下是「概念 → 特性 → 实现」的层次关系。你记住一个核心:Delta Lake 的一切能力,都建立在事务日志和 Parquet 文件之上。后面的实时写入 Kafka 方案,也是在这个基础上搭建的。