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

132 阅读8分钟

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

1.发展历史

数据湖三阶段:Hadoop、Hive、湖仓一体

①数据湖发展阶段1-Hadooop

image.png

数据湖最开始的概念——分布式存储HDFS

使用目录来区分不同的数据集

好处

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

坏处(数据沼泽)

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

②数据湖发展阶段2-Hive

数据湖的演进-Hive Metastore(将元数据(列名等位置信息)存在mysql中)

对数据湖中的数据集进行集中定义

  • 数据湖中存在了哪些数据集
  • 它们都存储在什么目录
  • 数据集的schema是什么样子的
  • 数据集有哪些分区,每个分区的目录是什么

问题

  • 如果这张hive表是静态的,没有新增写入,则所有读取方都能很便捷的使用,但是如果读取方正在读的数据被另一方修改,将会读到脏数据

    • 需要Transaction ACID
  • 若hive中存储了一个schema,对于schema中列的操作只能追加,无法删除,因此想要删除列时,只能重写一张表(耗费资源)

    • 需要支持更多样的schema变更

③数据湖发展阶段3-湖仓一体

什么是数据仓库

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

image.png

数仓不支持ML,因为ML需要大量的原始数据,而数仓只存储结构化的数据

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

image.png

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

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

  • key features

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

④业界三大数据湖

  1. Uber:apache hudi(最初想解决的问题:使数据写入能够更低延迟)
  2. NETFLIX:iceberg(最初想解决的问题:摆脱Hive架构带来的问题+存储用户观看数据,推荐剧集)
  3. databricks:delta lake(未完全开源)(主打湖仓一体,代码简洁)

⑤总结

  • 数据相关概念比较新,一直处在演进当中
  • 一开始是HDFS,裸pb、txt日志等,叫数据湖(管不动了就叫数据沼泽)
  • 后来出现了iceberg、hudi、delta lake,数据湖概念就基本等于这些产品了
  • 也更贴近于Lakehouse的概念

2.核心技术

湖仓一体项目解决的关键问题:Transaction、Schema变更、冲突解决

①文件结构

image.png

②Time travel

image.png

要点

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

③Transaction(事务)

image.png

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

  • 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文件命名,如afdb582.json
    • 直到json文件内容写入完毕,利用hdfs的renameIfAbsent能力将afdb582.json替换成000006.json,到此为止commit完成,新的读取将会以000006.json作为最新版本
  • 读写冲突已经解决

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

事务隔离

image.png

Update写入流程

  1. 从最新的版本中,获取需要update的分区

  2. 乐观锁先把写入的文件全落盘,然后进入写json阶段

  3. 分两种情况

    1. 发现版本号和一开始没区别,直接写新的版本

    2. 发现版本号增加了,看看新增的这些版本有没有更新我要更新的分区

      1. 没有,直接写新的版本
      2. 有,两者都更新了同一分区,要重新update

④Schema Evolution

Add/Drop/Rename

  • 用户并不直接读取parquet文件本身,而是通过数据湖接口读取,如Dataset ds = simpleDataLake.read(mytable).option(date = 2020-01-01)
  • 数据湖内部会读取应该读的parquet,并在schema上做进一步处理

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

  1. 唯一确定的ID。新增列赋予新ID。删列ID不复用

  2. 写入数据时,ID也写入数据文件

  3. 读取数据时,用ID做映射,如果

    1. Data中没有,metadata中有:ADD(此时如果想读还没有写入的那一列的数据,可以赋一个缺省值)
    2. Data中有,metadata中没有:DROP(此时读就不要那一列:parquet文件是可以选列的(列存))
    3. Data和metadata中都有同一ID,但是name不同:RENAME

3.各有所长

不同湖仓一体项目的独到之处

①Iceberg

工作重点

  • 用户体验

    • Schema evolution
    • Partition evolution
    • Hidden partition
    • TIme Travel
    • Version Rollback
  • 性能

    • 快速file plan
    • 更多的filter方式
  • 可靠性

    • ACID Transaction
  • 完全开源,由Apache孵化开发

Well-designed Metadata Layer

image.png

  • Metadata files定义了表结构,存储了snapshot版本号信息,分区信息等
  • Manifest lists存储了一个snapshot中所有manifest的信息
  • Manifests存储了一些data files 的信息
  • Data files就是具体的数据文件

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

  1. Manifest file记录了每个data file的分区范围
  2. Manifest list记录了每个,manifest file的分区范围,分区可以被快速定位,可以做manifest list级别裁剪
  3. Manifest file记录了每个data file每一列的最大值,最小值可以通过其他的列(UserId)做data file级别裁剪

Hidden Partition

  • 传统的分区方式:数据中包含了data列,则按照data分区;如果希望hour分区,则需要新增hour列

  • Iceberg分区方式

    • 数据中包含timestamp列,设置好partition transform方式

      • 设置为data时,iceberg帮你转化为data分区
      • 设置为hour时,iceberg帮你转化为hour分区
      • Iceberg记录了这层转化关系,并且按你的需要进行partition evolution

②Hudi(Hadoop Upsert Delete and Increment)

Hudi工作重点

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

Timeline Service & Upsert & Incremental

image.png

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

Copy on Write

  • 在upsert数据时,要把涉及到的分区全部读出来进行加工,再全部写回去(带来资源浪费)

Merge on Read

  • 分区存两个表:base table 和 increment table,增表记录每一次更新的PK列和对应的更新列,新upsert的数据与之对比

③Delta Lake工作重点

  1. ACID Transaction
  2. Schema 校验(不是evolution)
  3. 流批一体
  4. Time travel
  5. Upsert/Delete
  6. Z-order优化
  7. 只开源了一部分,由Databricks自己主导开发,Z-order等优化的实现未开源

流批一体

image.png

4.总结场景

三个湖仓一体项目的对比,数据湖再字节跳动中的应用

①三个数据湖的异同

image.png

②三个湖的热度

image.png

③技术选型

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

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

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

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

⑤总结

  1. 数据湖的最新发展状态是湖仓一体
  2. 数据湖以低存储成本提供了ACID Transaction、Scheme evolution、Time travel等高级功能