这是我参与「第四届青训营 」笔记创作活动的第2天
概述
本节课程主要分为 4 个方面:
- 数据湖三阶段:Hadoop、Hive、湖仓一体
- 湖仓一体项目核心问题:Transction、Schema变更、冲突解决
- 不同湖仓一体项目的独到之处
- 三个湖仓一体的项目对比,数据湖在字节的应用
数据湖发展阶段
- Hadoop
数据湖最开始的概念来自于分布式存储 HDFS,使用目录来区分不同的数据集。数据上云,只需要知道具体的目录即可,同时也易于横向扩展。
好处:
- 同一公司/组织可以使用共享存储;
- 数据访问方便,灵活性高。
坏处 -数据沼泽:
- 没有记录文件的 schema(包括列名、列类型),使用 Schema on Query 的方式;
- 难以得知数据集包含了那些文件,是通过什么样的分区组织的;
- 如果多个程序都在修改这个数据集(修改数据、修改表结构),其他程序难以配合做修改
2. Hive
数据湖的演进 -Hive Metastore,是对数据湖中的数据集进行集中“定义”:
- 数据湖中存在了哪些数据集
- 它们都存储在什么目录
- 数据集的schema是什么样子的
- 数据集有哪些分区,每个分区的目录是什么
如果这张 hive 表是静态的,没有新增写入,则所有读取方都能很便捷的使用,但存在以下两个不足:
- 假设 Reader A 和 B 都正在读取分区 /20220623 下的文件:
此时 Writer B 开始重写该分区,部分删除、增加或修改,则 Reader A 和 B 读到的文件都可能不同
2. 分区 /20220623 下存储了 schema 为 date | userld | phoneNumber 的数据:
需要注意:Hive allows us to add column after last column only;根据合规,需删掉 phoneNumber,但是在 Hive 表上做不到,只能重写(耗费资源)
所以分别需要引入 Transaction ACID,和更多样的 schema 变更。
3. 湖仓一体 Lakehouse
-
数据仓库将数据从数据源提取和转换,加载到目的地;
-
数据仓库存储+计算不分离;
-
数据仓库严格控制写入数据的schema
数据仓库
数据湖(一阶段)
成本
高
低
存储计算分离
否
是
ACID
是
否
湖仓一体的现状:
- 结合了数据湖和数据仓库的优势
- 将数据仓库中对于数据的严格管理直接实现到了低成本的分布式存储之上
- 关键特征:Transaction ACID,Schema 管理,存储计算分离,支持多种计算引擎和文件格式
4. 业界三大数据湖
a. Hudi: Uber旗下,在行程规划、拼车、顺风车等业务上数据量很大,数据写入延迟较大;2016年开源
b. Iceberg: Netfix旗下,流媒体存储用户观看数据,推荐剧集等,希望摆脱 Hive 架构存在的问题;2018年开源
c. Delta Lake: Databricks旗下,起源于UCB,曾参与 Spark 制作的超级独角兽,主打湖仓一体;2019年部分开源
核心技术点
1. 简单设计一个数据湖
- 存一些按照 date 分区的 schema 是 userId,date,event,phone
- 每天都写入新数据
- 需要使用列存格式
**解决方案:**写入数据湖时
-
按照每条数据的 date 进行分区
-
额外使用 metadata 文件记录表信息
2. Time travel
- 每次写入都生成一个新的元数据文件,记录变更
- 分区数据 Update 时,不要删除l旧数据,保证新旧共存
- 元数据中存储具体的文件路径,而不仅仅是分区文件夹
解决方案:
- 每一次写入操作,创建一个新的 json 文件,以递增版本号命名,记录本次新增/删除的文件
- 每当产生 N 个 json,做一次聚合,记录完整的分区文件信息
- 用 checkpoint 记录上次做聚合的版本号
3. Transaction
ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。在数据湖中体现为:
-
Atomicity 原子性:本次写入要么对用户可见,要么不可见(需要设计)
-
写入流程
- 写入 parquet 数据文件
- 写入 json 元数据文件
- 如何确保原子性(从用户可见性入手)
- 用户只会读取以版本号数字命名的 json 文件,每次都读取到最大的版本号作为数据集的现状
- 新的写入写完 parquet 后开始写 json 文件,使用 hash 值对 json 文件命名,如 a2fs4hfg8ee.json
- 直到 json 文件内容写入完毕,利用 hdfs 的 renameIfAbsent 能力将a2fs4hfg8ee.json 替换为 000006.json,到此为止 commit 完成,新的读取将会以 000006.json 作为最新版本
- 读写冲突已解决
- 新的写入除非已经 commit,否则用户读不到
- 用户正在读的分区,被另一个写入进行了更新,数据不会进行替换,而是共存
- Consistency 一致性:输入是什么,落盘的就是什么(由计算引擎保证)
- Isolation事务隔离:正确解决读写冲突和写写冲突(需要设计)
- Durability:持久性 -落完数据后,即便服务器重启结果不变(由存储引擎保证)
各数据湖的优势
Iceberg
-
工作重点:
-
用户体验
-
Schema evolution
-
Partition evolution
-
Hidden partition
-
Time Travel
-
Version Rollback
-
性能
-
快速 file plan
-
更多的 filter 方式
-
可靠性
-
ACID Transaction
-
完全开源,由Apache孵化开发
-
设计架构:
-
Metadata files 定义了表结构,存储了 snapshot 信息,分区列信息等
-
Manifest lists 存储了一个 snapshot 中所有 manifest 的信息
-
Manifests 存储了一些 data files 的信息
-
Data files 就是具体的数据文件
- 优势:有助于filter的数据被层层记录,分区可以被快速定位,可以做 manifest list 级别裁剪
- Manifest file记录了每个data file的分区范围
- Manifest list记录了每个manifest file的分区范围
- Manifest file记录了每个data file每一列的最大值,最小值可以通过其他的列 Userld 做 data file 级别裁剪
3. Hidden Partition:
- 传统的分区方式:数据中包含了 date 列,则按照 date 分区;如果希望按照 hour 分区,则需要新增列
- Iceberg的分区方式:数据中包含 timestamp 列,设置 partition transform 方式(设置为 date 时,转化为 date 分区;设置为 hour 时,转化为 hour 分区),Iceberg 记录转化关系,并按需进行 partition evolution。
Hudi
- 工作重点:
Hudi -Hadoop Upsert Delete and Incremental
-
Timeline service:Hudi 管理 transaction 的方式(对标 Metadata)
-
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:
Copy on Write:读出来之后再更新,复制会导致资源的浪费
Merge on Read:只需要将 PK 和更新的列并入小文件,节省了存储资源
Delta Lake
1. 工作重点:
-
ACID Transaction
-
Schema 校验(不是 evolution)
-
流批一体
-
Time Travel
-
Upsert / Delete
-
Z-Order 优化
-
只开源了一部分,由 Databricks 主导开发,Z-order 等优化的实现未开源
2. 流批一体:
每一次的写入都生成一个新的 Json 文件,也可以存储 checkpoint 等等信息,后期可以用 Spark 等进行读取,方便用户使用。