数据湖三剑客:Delta Lake、Hudi 与 Iceberg| 青训营笔记
# 数据湖三剑客:Delta Lake、Hudi 与 Iceberg| 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第 11 天
# 数据湖发展历史
# 数据湖发展阶段 1 - Hadoop

- 数据湖最开始的概念 -- 分布式存储 HDFS
- 使用目录来区分不同的数据集
- /douyin
- /20220810
- /20220811
- /toutiao
- /douyin
优点:
- 同一组织可以使用共享存储
- 数据访问方便,灵活性高
缺点:
- 没有记录文件的 schema (包括列名、列类型),经常使用 Schema on Query 的方式
- 难以得知数据集包含了那些文件,是通过什么样的分区组织的
- 如果多个程序都在修改这个数据集(修改数据、修改表结构),其他程序难以配合做修改
# 数据湖发展阶段 2 - Hive

- 数据湖的演进 -- Hive Metastore
- 对数据湖中的数据集进行集中 “定义”
- 数据湖中存在了哪些数据集
- 它们都存储在什么目录
- 数据集的 schema 是什么样子的
- 数据集有哪些分区,每个分区的目录是什么
如果这张 hive 表是静态的,没有新增写入,则所有读取方都能很便捷的使用
问题来了:
- ①假设 Reader A 和 B 都正在读取分区 / 20220623 下的文件
- 此时 Writer B 开始重写 / 20220623 分区,一些文件被删了,一些文件增加了,一些文件还在修改中
- Reader A 和 B 读到的文件可能是不同的!
我们需要 Transaction ACID!
问题又来了:
- 分区 / 20220623 下存储了 schema 为 date l userld l phoneNumber 的数据
- 需要注意:Hive allows us to add column after last column only
- 根据合规,我需要删掉 phoneNumber,但是在 Hive 表上做不到。只好重写一张表(耗费资源)
我们需要支持更多样的 schema 变更!以及更多的功能
# 数据湖发展阶段 3 - 湖仓一体
什么是数据仓库?

- 数据仓库将数据从数据源提取和转换,加载到目的地
- 数据仓库存储 + 计算不分离
- 数据仓库严格控制写入数据的 schema
| 数据仓库 | 数据湖(阶段 2) | |
|---|---|---|
| 成本 | 高 | 低 |
| 存储计算分离 | 否 | 是 |
| ACID | 是 | 否 |
湖仓一体(数据湖的现状):

结合了数据湖和数据仓库的优势
将数据仓库中对于数据的严格管理直接实现到了低成本的分布式存储之上
Key Features:
- Transaction ACID
- Schema 管理
- 存储计算分离
- 支持多种计算引擎和文件格式
# 业界三大数据湖
- Uber 开源的 Hudi
- NETFLIX 开源的 Iceberg
- databricks 开源的 DELTA LAKE### 关于 “数据湖”
- 数据相关概念比较新,一直处在演进当中
- 一开始是 HDFS,裸 pb、txt 日志等等,叫数据湖(管不动了就叫数据沼泽)
- 后来出现了了 lceberg、Hudi、Delta Lake 了,数据湖概念就基本等于这些产品了
- 也更贴近于 Lakehouse 的概念
# 核心技术
# 文件结构
- 我想存一些数据,按照 date 分区 schema 是 userld、date、event、phone
- 每天都会写入新数据
- 需要使用列存格式

写入数据湖时
- 按照每条数据的 date 进行分区
- 额外使用 metadata 文件记录表信息

# Time travel
要点:
- 每次写入都生成一个新的元数据文件,记录变更
- 分区数据在 Update 时,不要删除旧数据,保证新旧共存
- 元数据中存储具体的文件路径,而不仅仅是分区文件夹

- 每一次写入操作,创建一个新的 json 文件,以递增版本号命名,记录本次新增 / 删除的文件。
- 每当产生 N 个 json,做一次聚合,记录完整的分区文件信息
- 用 checkpoint 记录上次做聚合的版本号

# Transaction
ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。
以 A 给 B 转账 10 元为例:
- Atomicity: 原子性 —— 要么 A-10 B+10,要么都不变
- Consistency: 一致性 —— 不可以 A-10 B+5
- Isolation: 事务隔离 ——A 和 C 同时给 B 转 10,B 最终结果应是 + 20
- Durability: 持久性 —— 转账服务器重启,结果不变
数据湖中的 ACID:
- Atomicity: 原子性 - 本次写入要么对用户可见,要么不可见(需要设计)
- Consistency: 一致性 - 输入是什么,落盘的就是什么(由计算引擎保证)
- Isolation: 事务隔离一正确解决读写冲突和写写冲突(需要设计)
- Durability: 持久性 - 落完数据后,即便服务器重启结果不变(由存储引擎保证)
# 原子性
写入流程:
- 写入 parquet 数据文件
- 写入 json 元数据文件
如何确保原子性?(从用户可见性入手!)
- 用户只会读取以版本号数字命名的 json 文件,每次都读取到最大的版本号作为数据集的现状
- 新的写入写完 parquet 后开始写 json 文件,使用 hash 值对 json 文件命名,如 a2fs4hfg8ee.json
- 直到 json 文件内容写入完毕,利用 hdfs 的 renamelfAbsent 能将 a2fs4hfg8ee.json 替换为 000006.json,到此为止 commit 完成,新的读取将会以 000006.json 作为最新版本
读写冲突已经被解决,how?
- 新的与入除非已经 commit,否则用户读不到
- 用户正在读的分区,被另一个写入进行了更新,数据不会进行替换,而是共存
# 事务隔离
| 写写冲突 | Insert | Update/delete |
|---|---|---|
| insert | Can not conflict | |
| Update/delete | Can conflict | Can conflict |
Update 写入流程:
- 从最新的版本中,获取需要 update 的分区
- 乐观锁先把该写入的文件全落盘,然后进入写 json 阶段
- 分几种情况:
- 发现版本号和一开始没区别,直接写新的版本。
- 发现版本号增加了,看看新增的这些版本有没有更新我要更新的分区?
- 没有,直接写新的版本。
- 有,两者都更新了同一分区,得重新 update 了。
# Schema Evolution
ID 将 data 和 metadata 的列名做一一对应!
- 唯一确定的 ID。新增列赋予新 ID。删列 IID 不复用。
- 写入数据时,ID 也写入数据文件
- 读取数据时,用 ID 做映射,如果
- Data 中没有,metadata 中有: ADD
- Data 中有,metadata 中没有: DROP
- Data 和 metadata 中都有同一 ID,但是 name 不同: RENAME
- 如果都有同一列名,而 ID 不同?
- 某一列删除了,然后重新加入该列名
# 各有所长
# lceberg 工作重点
- 用户体验
- Schema evolution
- Partition evolution
- Hidden partition
- Time Travel
- Version Rollback
- 性能
- 快速 file plan
- 更多的 filter 方式
- 可靠性
- ACID Transaction
- 完全开源,由 Apache 孵化开发
# Well-designed Metadata Layer

- Metadatafiles 定义了表结构,存储了 snapshot 信息,分区列信息等
- Mantes Hsts 存储了一个 5napshpt 中所有 manirest 的信息
- Mani fests 存储了一些 data files 的信息
- Data files 就是具体的数据文件
# Data File Filter
一些有助于 filter 的数据被层层记录,比如:
- Manifestfile 记录了每个 data file 的分区范围
- Manifestlist 记录了每个 manifestfile 的分区范围分区可以被快速定位!可以做 manifest list 级别裁剪。
- Manifest file 记录了每个 data file 每一列的最大值,最小值可以通过其他的列 (Userld)做 data file 级别裁剪。
# Hidden Partition
传统的分区方式:
- 数据中包含了 date 列,则按照 date 分区;如果希望按照 hour 分区,则需要新增 hour 列
Iceberg 的分区方式:
- 数据中包含 timestamp 列,设置好 parttion transform 方式
- 设置为 date 时,iceberg 帮你转化为 date 分区
- 设置为 hour 时,iceberg 帮你转化为 hour 分区
- Iceberg 记录了这层转化关系,并且按你的需要进行 partition evolution
# Hudi 工作重点
Hudi(Hadoop Upsert Delete and Incremental)
工作重点:
- Timeline service:Hudi 管理 transaction 的方式
- Hudi Table Type: Copy on Write / Merge on Read
- 高效的 Upserts: update or insert
- 索引表:快速定位一条数据的位置
- Streaming Ingestion Service
- 完全开源,由 Apache 孵化
# Timeline Serivce & Upsert & Incremental

- Timeline Service:记录的信息类似于 metadata
- Upsert:每一条样本都有一个主键 PK, 当 Upsert 一条数据时,如果现有的数据中有这个 PK,则 update 这条数据。否则直接 insert 这条数据
- Incremental:某个时间点后新增的数据
# Copy on Write

# Merge On Read

# Delta Lake 工作重点
- ACID Transaction
- Schema 校验 (不是 evolution)
- 流批一体
- Time Travel
- Upsert/Delete
- Z-Order 优化
- 只开源了一部分,由 Databricks 自己主导开发,Z-Order 等优化的实现未开源
# 流批一体

# 总结场景

# 三个数据湖的热度

# 技术选型
短期来看:每个项目部有一些展于自己的功能:
- 如果强需求 Upserts,也许 Hudi 是最好的选择
- 如果希望可扩展性强,那么设计优良的 Iceberg 是最好的选择
- 如果希望用上 Z-Order 等优化,那么掏钱买 Databricks 的企业版 Delta 是不二之选
长期来看:数据湖取代 Hive,成为 HDFS 上的表格式标准是必然的,在选择之前问自己四个问题:
- 我需要的 feature 在哪个数据湖上是最稳定的
- 哪一个数据湖能够用最简单的接入方式(SQL)用上最完善的功能
- 哪一个数据湖有计算引擎侧的支持和社区的支持
- 哪一个数据湖的版本管理做的最好,最鲁棒
编辑 (opens new window)
上次更新: 2023/12/06, 01:31:48