数据湖三剑客:Delta Lake、Hudi 与 Iceberg 详解
这是我参与「第四届青训营」笔记创作活动的的第十一天
1.发展历史
数据湖三阶段:Hadoop、Hive、湖仓一体
1.Hadoop
HDFS-分布式存储系统: HDFS通过将文件分块来存储大文件,HDFS的组件有NameNode和 DataNode,分别负责提供元数 据和数据服务。在读/写数据时,HDFS客户端需要先从 NameNode 上获取数据读取/写入的 DataNode地址,然后和DataNode交互来完成数据读/写。 优点:
- 可以实现分布式存储
- 不同目录区分数据集,
缺点:
- 不能做到多个人同时去修改HDFS里的数据
- 无法知道HDFS里数据schema是什么样的
2.Hive
优点:
- 相比于HDFS,可以知道HDFS里的数据格式schema是什么样的 缺点:
- 建好表以后,不能对schema进行变更,比如删掉一列这种操作
- 依然不能多人同时修改、读写
3.湖仓一体(数据湖的现状)
- 结合了数据湖和数据仓库的优势
- 将数据仓库中对于数据的严格管理直接实现到了低成本的分布式存储之上
- Key Features:
- Transaction ACID
- Schema管理
- 存储计算分离
- 支持多种计算引擎和文件格式
4.数据仓库
- 数据仓库将数据从数据源提取和转换,加载到目的地
- 数据仓库存储+计算不分离
- 数据仓库严格控制写入数据的schema
5.数据湖
- 数据相关概念比较新,一直处在演进当中
- 一开始是HDFS,裸pb、txt 日志等等,叫数据湖(管不动了就叫数据沼泽)
- 后来出现了了Iceberg、Hudi、 Delta Lake了,数据湖概念就基本等于这些产品了
- 也更贴近于Lakehouse的概念
2.核心技术
1.Time travel
要点:
- 每次写入都生成-一个新的元数据文件,记录变更
- 分区数据在Update时,不要删除旧数据,保证新旧共存
- 元数据中存储具体的文件路径,而不仅仅是分区文件夹 操作:
- 每一次写入操作,创建一个新的json文件,以递增版本号命名,记录本次新增/删除的文件。
- 每当产生N个json,做一次聚合,记录完整的分区文件信息
- 用checkpoint记录上次做聚合的版本号
2.Transaction
ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。
1.以A给B转账10元为例:
- Atomicity: 原子性 ———— 要么A-10 B+10,要么都不变
- Consistency: 一致性 ———— 不可以A-10 B+5
- Isolation: 事务隔离 一 A和C同时给B转10,B最终结果应是+20
- Durability: 持久性 - 转账服务器重启,结果不变
2.数据湖中的ACID
- Atomicity: 原子性-本次写入要么对用户可见,要么不可见(需要设计)
- Consistency: 一致性-输入是什么,落盘的就是什么(由计算引擎保证)
- Isolation: 事务隔离-正确解决读写冲突和写写冲突(需要设计)
- Durability: 持久性-落完数据后,即便服务器重启结果不变(由存储引擎保证)
3.原子性
1.写入流程
- 写入parquet数据文件
- 写入json元数据文件
2.确保原子性
- 用户只会读取以版本号数字命名的json文件,每次都读取到最大的版本号作为数据集的现状
- 新的写入写完parquet后开始写json文件,使用hash值对json文件命名,如a2fs4hfg8ee.json
- 直到json文件内 容写入完毕,利用hdfs的renameIfAbsent能力将a2fs4hfg8ee.json替换为000006.json,到此为止commit完成,新的读取将会D以000006.json作为最新版本
3.读写冲突解决方式
- 新的写入除非已经commit,否则用户读不到
- 用户正在读的分区,被另一个写入进行了更新,数据不会进行替换,而是共存
4.事务隔离
Update写入流程:
- 从最新的版本中,获取需要update的分区
- 乐观锁先把该写入的文件全落盘,然后进入写json阶段
- 分几种情况:
- 发现版本号和一 开始没区别, 直接写新的版本。
- 发现版本号增加了,看看新增的这些版本有没有更新我要更新的分区
- 没有, 直接写新的版本。
- 有,两者都更新了同一分区,得重新update了。
5.Schema Evolution
- 用户并不直接读取parquet文件本身,而是通过数据湖接口读取,如Dataset ds = simpleDataLake.read(mytable) .option(date=2020-01-01)
- 数据湖内 部会读取应该读的parquet,并在schema上做进一步处理 ID将data和metadata的列名做一一对应!
- 唯一确定的D。新增列赋予新ID。删列ID不复用。
- 写入数据时,ID也写入数据文件
- 读取数据时,用ID做映射,如果
- Data中没有,metadata中有: ADD
- Data中有,metadata中没有: DROP
- Data和metadata中 都有同一ID,但是name不同: RENAME而不同
3.三大数据湖的特点
1.Iceberg
1.工作重点
用户体验
- Schema evolution
- Partition evolution
- Hidden partition
- Time Travel
- Version Rollback 性能
- 快速file plan
- 更多的filter方式 可靠性
- ACID Transaction 完全开源,由Apache孵化开发
2.Well-designed Metadata Layer
- Metadata files 定义了表结构,存储了snapshot信息,分区列信息等
- Manifest lists存储了一个snapshot中所有manifest的信息
- Manifests存储了一些data files 的信息
- Data files就是具体的数据文件
3.Data File Filter
一些有助于filter 的数据被层层记录,比如:
- Manifest file 记录了每个data file 的分区范围
- Manifest list 记录了每个manifest file 的分区范围分区可以被快速定位!可以做manifest list 级别裁剪。
- Manifest file 记录了每个data file 每一列的最大值,最小值可以通过其他的列(UserId) 做data file 级别裁剪。
4.Hidden Partition
传统的分区方式:
数据中包含了date列,则按照date分区;如果希望按照hour分区,则需要新增hour列
Iceberg的分区方式: 数据中包含timestamp列,设置好partition transform方式
- 设置为date时,iceberg 帮你转化为date分区
- 设置为hour时,iceberg 帮你转化为hour分区
- Iceberg记录了这层转化关系,并且按你的需要进行partition evolution
2.Hudi
1.工作重点
- Timeline service: Hudi 管理transaction的方式
- Hudi Table Type: Copy on Write / Merge on Read
- 高效的Upserts: update or insert
- 索引表:快速定位一条数据的位置
- Streaming Ingestion Service
- 完全开源,由Apache孵化
2.Timeline Serivce & Upsert & Incremental
- Timeline Service: 记录的信息类似于metadata
- Upsert:每-条样本都有一个主键PK,当Upsert一条数据时,如果现有的数据中有这个PK,则update这条数据。否则直接insert这条数据
- Incremental: 某个时间点后新增的数据
2.Copy on Write
3.Merge On Read
3.Delta Lake
工作重点
- ACID Transaction
- Schema校验(不是evolution)
- 流批一体
- Time Travel
- Upsert/Delete
- Z-Order优化
- 只开源了一部分,由Databricks自己主导开发,Z-order 等优化的实现未开源