数据湖三剑客:Delta Lake、Hudi 与 Iceberg| 青训营笔记

104 阅读8分钟

数据湖三剑客:Delta Lake、Hudi 与 Iceberg| 青训营笔记

这是我参与「第四届青训营 」笔记创作活动的的第11天

数据湖发展历史

数据湖发展阶段1 - Hadoop

image-20220811022824549.png

  • 数据湖最开始的概念--分布式存储HDFS
  • 使用目录来区分不同的数据集
    • /douyin
      • /20220810
      • /20220811
    • /toutiao

优点:

  • 同一组织可以使用共享存储
  • 数据访问方便,灵活性高

缺点:

  1. 没有记录文件的schema (包括列名、列类型),经常使用Schema on Query的方式
  2. 难以得知数据集包含了那些文件,是通过什么样的分区组织的
  3. 如果多个程序都在修改这个数据集(修改数据、修改表结构),其他程序难以配合做修改

数据湖发展阶段2 - Hive

image-20220811022936439.png

  • 数据湖的演进 -- 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 - 湖仓一体

什么是数据仓库?

image-20220811023442701.png

  • 数据仓库将数据从数据源提取和转换,加载到目的地
  • 数据仓库存储+计算不分离
  • 数据仓库严格控制写入数据的schema

数据仓库数据湖(阶段2)
成本
存储计算分离
ACID

湖仓一体(数据湖的现状):

image-20220811023801599.png

  • 结合了数据湖和数据仓库的优势

  • 将数据仓库中对于数据的严格管理直接实现到了低成本的分布式存储之上

  • Key Features:

    • Transaction ACID
    • Schema 管理
    • 存储计算分离
    • 支持多种计算引擎和文件格式

业界三大数据湖

  • Uber开源的Hudi

  • NETFLIX开源的Iceberg

  • databricks开源的DELTA LAKE

    关于“数据湖”

  • 数据相关概念比较新,一直处在演进当中

  • 一开始是HDFS,裸pb、txt 日志等等,叫数据湖(管不动了就叫数据沼泽)

  • 后来出现了了 lceberg、Hudi、Delta Lake了,数据湖概念就基本等于这些产品了

  • 也更贴近于 Lakehouse 的概念

核心技术

文件结构

  1. 我想存一些数据,按照date分区 schema是userld、date、event、phone
  2. 每天都会写入新数据
  3. 需要使用列存格式

image-20220811024828288.png

写入数据湖时

  1. 按照每条数据的date进行分区
  2. 额外使用metadata 文件记录表信息 image-20220811024839886.png

Time travel

要点:

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

image-20220811025201606.png

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

image-20220811025248687.png

Transaction

ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。

以A给B转账10元为例:

  1. Atomicity:原子性——要么A-10 B+10,要么都不变
  2. Consistency:一致性——不可以A-10 B+5
  3. Isolation:事务隔离——A和C同时给B转10,B最终结果应是+20
  4. Durability:持久性 ——转账服务器重启,结果不变

数据湖中的ACID:

  1. Atomicity:原子性-本次写入要么对用户可见,要么不可见(需要设计)
  2. Consistency:一致性-输入是什么,落盘的就是什么(由计算引擎保证)
  3. Isolation:事务隔离一正确解决读写冲突和写写冲突(需要设计)
  4. Durability:持久性-落完数据后,即便服务器重启结果不变(由存储引擎保证)

原子性

写入流程:

  1. 写入 parquet 数据文件
  2. 写入json元数据文件

如何确保原子性?(从用户可见性入手!)

  1. 用户只会读取以版本号数字命名的json文件,每次都读取到最大的版本号作为数据集的现状
  2. 新的写入写完parquet后开始写json文件,使用hash值对json文件命名,如a2fs4hfg8ee.json
  3. 直到json文件内容写入完毕,利用hdfs的renamelfAbsent能将a2fs4hfg8ee.json替换为000006.json,到此为止commit完成,新的读取将会以000006.json作为最新版本

读写冲突已经被解决,how?

  1. 新的与入除非已经commit,否则用户读不到
  2. 用户正在读的分区,被另一个写入进行了更新,数据不会进行替换,而是共存

事务隔离

写写冲突InsertUpdate/delete
insertCan not conflict
Update/deleteCan conflictCan conflict

Update 写入流程:

  1. 从最新的版本中,获取需要 update 的分区
  2. 乐观锁先把该写入的文件全落盘,然后进入写 json 阶段
  3. 分几种情况:
    1. 发现版本号和一开始没区别,直接写新的版本。
    2. 发现版本号增加了,看看新增的这些版本有没有更新我要更新的分区?
      1. 没有,直接写新的版本。
      2. 有,两者都更新了同一分区,得重新 update了。

Schema Evolution

ID 将 data 和 metadata 的列名做一一对应!

  1. 唯一确定的ID。新增列赋予新ID。删列IID不复用。
  2. 写入数据时,ID也写入数据文件
  3. 读取数据时,用ID做映射,如果
    1. Data中没有,metadata中有: ADD
    2. Data中有,metadata中没有: DROP
    3. Data和metadata中都有同一ID,但是name不同: RENAME
    4. 如果都有同一列名,而ID不同?
      • 某一列删除了,然后重新加入该列名

各有所长

lceberg 工作重点

  • 用户体验
    • Schema evolution
    • Partition evolution
    • Hidden partition
    • Time Travel
    • Version Rollback
  • 性能
    • 快速file plan
    • 更多的filter方式
  • 可靠性
    • ACID Transaction
  • 完全开源,由Apache孵化开发

Well-designed Metadata Layer

image-20220811032513298.png

  • Metadatafiles定义了表结构,存储了 snapshot 信息 ,分区列信息等
  • Mantes Hsts 存储了一个 5napshpt 中所有 manirest 的信息
  • Mani fests存储了一些data files 的信息
  • Data files 就是具体的数据文件

Data File Filter

一些有助于 filter 的数据被层层记录,比如:

  1. Manifestfile记录了每个data file 的分区范围
  2. Manifestlist记录了每个manifestfile的分区范围分区可以被快速定位!可以做 manifest list 级别裁剪。
  3. 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)

工作重点:

  1. Timeline service:Hudi管理transaction的方式
  2. Hudi Table Type: Copy on Write / Merge on Read
  3. 高效的 Upserts: update or insert
  4. 索引表:快速定位一条数据的位置
  5. Streaming Ingestion Service
  6. 完全开源,由Apache 孵化

Timeline Serivce & Upsert & Incremental

image-20220811233616084.png

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

Copy on Write

b3b203a1-2680-4d0a-80ce-8e295c477c10.png

Merge On Read

005c2dc0-7a83-4cf3-b4b7-1f70ebd5946f.png

Delta Lake 工作重点

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

流批一体

image-20220812184413861.png

总结场景

image-20220812184456890.png

三个数据湖的热度

image-20220812184625295.png

技术选型

短期来看:每个项目部有一些展于自己的功能:

  • 如果强需求Upserts,也许Hudi是最好的选择
  • 如果希望可扩展性强,那么设计优良的 Iceberg是最好的选择
  • 如果希望用上 Z-Order 等优化,那么掏钱买Databricks 的企业版Delta 是不二之选

长期来看:数据湖取代Hive,成为HDFS上的表格式标准是必然的,在选择之前问自己四个问题:

  1. 我需要的feature 在哪个数据湖上是最稳定的
  2. 哪一个数据湖能够用最简单的接入方式(SQL)用上最完善的功能
  3. 哪一个数据湖有计算引擎侧的支持和社区的支持
  4. 哪一个数据湖的版本管理做的最好,最鲁棒