大数据-复习笔记-11 | 青训营笔记

179 阅读10分钟

数据湖三剑客:Delta Lake、Hudi 与 Iceberg 详解 - 复习笔记

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

1. 发展历史

1.1 数据湖发展阶段- Hadoop

image.png

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

1.2 数据湖发展阶段2- Hive

  • 数据湖的演进—Hive Metastore

  • 对数据湖中的数据集进行集中“定义”

    • 数据湖中存在了哪些数据集
    • 它们都存储在什么目录
    • 数据集的schema是什么样子的
    • 数据集有哪些分区,每个分区的目录是什么
  • 如果这张hive表是静态的,没有新增写入,则所有读取方都能很便捷的使用

  • 问题来了:

    • ①假设Reader A和B都正在读取分区/20220623下的文件
    • 此时Writer B开始重写/20220623分区,一些文件被删了,一些文件增加了,一些文件还在修改中
    • Reader A和B读到的文件可能是不同的!
  • 我们需要Transaction ACID!

  • 问题又来了:

    • 分区/20220623下存储了schema为date | userld |phoneNumber的数据
    • 需要注意:Hive allows us to add column after last column only
    • 根据合规,我需要删掉phoneNumber,但是在Hive表上做不到。只好重写一张表(耗费资源)
  • 我们需要支持更多样的schema变更!And more!

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

image.png

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

image.png

image.png

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

  • 结合了数据湖和数据仓库的优势
  • 将数据仓库中对于数据的严格管理直接实现到了低 成本的分布式存储之上
  • Key Features:
    • Transaction ACID
    • Schema管理
    • 存储计算分离
    • 支持多种计算引擎和文件格式

1.4 业界三大数据湖

  1. Uber
    公司简介:
    国外版滴滴,打车软件
    在行程规划、拼车、顺风车等业务上数据量都很大
    数据写入面临挑战

    项目简介:
    项目名称:Hudi
    最初希望解决的问题:使数据写入能够更低延迟
    开源时间:2016年
    官网:hudi.apache.org/
    Github:github.com/apache/hudi

  2. NETFLIX
    公司简介:
    流媒体
    《纸牌屋》、《鱿鱼游戏》、《怪奇物语》
    开发工作:存储用户观看数据,推荐剧集

    项目简介:
    名称:Iceberg
    开源时间:2018
    最初希望解决的问题:拜托Hive架构带来的问题中
    官网:iceberg.apache.org/
    Github:github.com/apache/iceb…

  3. databricks
    公司简介:
    起源于UCB,参与Spark的制作
    超级独角兽
    主打湖仓体

    项目简介:
    项目名称:Delta Lake
    开源时间:2019(开源了,但没完全开源)
    官网:delta.io/
    Github:github.com/delta-io/de…

1.5 关于“数据湖”

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

2. 核心技术

2.1 文件结构

image.png

写入数据湖时

  1. 按照每条数据的 date 进行分区
  2. 额外使用 metadata 文件记录表信息,如下代码段所示
{
    "schema";[
        {
            "name":"userID",
            "type":"int"
        },
        {
            "name":"date",
            "type":"string"
        },
        {
            "name":"event",
            "type":"string"
        },
        {
            "name":"phoneNumber",
            "type":"string"
        }
    ],
    "partition_co1":"date",
    "location":"mytable/",
    "partition":[
        "2020-81-01",
        "2020-01-02"
    ]
}

2.2 Time travel

要点:

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

image.png

  1. 每一次写入操作,创建一个新的json文件,以递增版本号命名,记录本次新增/删除的文件。
  2. 每当产生N个json,做一次聚合,记录完整的分区文件信息
  3. 用checkpoint记录上次做聚合的版本号
{
    "schena":[
        {
            "nane":"userTD",
            "type":"int"
        },
        {
            "nane":"date",
            "type":"string"
        },
        {
            "nane":"event",
            "type":"string"
        },
        {
            "nane":"phonelunber",
            "type":"string"
        },
    ],
    "partition_col":"date",
    "Location":"mytable/",
    "partition":[
        {
            "2020-01-01":[
                "xxx.parquet",
                "yyy.parquet"
            ],
            "2020-01-02":[
                "xxx.parquet",
                "yyy.parquet"
            ]
        }
    ]
}

2.3 Transaction

Transaction交易?事务!

ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。以A给B转账10元为例:

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

数据湖中的ACID:

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

2.3.1 原子性

image.png

为写入流程:

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

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

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

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

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

2.3.2 事务隔离

image.png

Update写入流程:

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

2.4 Schema Evolution

Add/Drop/Rename

重要:

  • 用户并不直接读取parquet文件本身,而是通过数据湖接口读取,如Datasetds= 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
    3. Data和metadata中都有同一ID,但是name不同:RENAME
    4. 如果部有同一列名,而D不同?

3. 各有所长

3.1 lceberg 工作重点

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

3.1.1 Well-designed Metadata Layer

image.png

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

3.1.2 Data File Filter

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

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

3.1.3 Hidden Partition

  • 传统的分区方式:
    • 数据中包含了date列,则按照date分区;如果希望按照hour分区,则需要新增hour列
  • Iceberg的分区方式:
    • 数据中包含timestamp列,设置好partition transform方式
      • 设置为date时,iceberg 帮你转化为date分区
      • 设置为hour时,iceberg 帮你转化为hour分区
      • 中Iceberg 记录了这层转化关系,并且按你的需要进行 partition evolution

3.2 Hudi

Hadoop Upsert Delete and Incremental

Hudi 工作重点:

  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孵化

3.2.1 Timeline Serivce & Upsert & Incremental

1659859634608.png

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

3.2.2 Copy on Write

1659859937033.png

3.2.3 Merge On Read

1659859999464.png

3.3 Delta Lake 工作重点

  1. ACID Transaction

  2. Schema校验(不是evolution)

  3. 流批一体

  4. Time Travel

  5. Upsert/Delete

  6. Z-Order 优化

  7. 只开源了部分,由Databricks 自己主导开发,Z-order等优化的实现未开源


4. 总结场景

4.1 回顾:三个数据湖的异同

1659860300546.png

4.2 三个数据湖的热度

1659860408236.png

1659860427587.png

4.3 技术选型

短期来看:每个项目部有“进门的功能:

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

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

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

4.3 字节跳动数据湖场景举例-Feature Store

Feature Store 在字节的诞生

  • 初心
  1. instance pb 样本读放大、不能列裁剪=>很难落许多特征进样本
  2. instance pb样本写放大、copy-on-write=>很难做特征回溯 调研
  • 收益
  1. 支持落原始特征做特征调研
  2. 解决读放大、支持列裁剪
  3. 解决写放大、支持merge-on-read
  4. 支持特征schema校验
  5. 列式存储优化存储空间
  6. Arrow 格式优化序列化开销

Feature Store 及其平台

  • 基于Apache lceberg做了大量定制与优化
  • 站内存储约220PB训练样本
    • 最大单表30PB,15K个特征
    • 相比instance pb样本能节省约30%~50%空间
  • 平台端到端体验完整、用户使用成本低
    • 控制面板、特征监控、数据维护(样本回溯、TTL、脏数据处理等)
  • 对特征回溯及特征调研场景做了专门强化
    • 支持Update 语义操作和数据分支

批流一体:近实时的图局入与数润就络(Raadiness)

  1. 例子:消息队列直接入湖、流式Upsert
  2. 应用:能同时训最新(流式)和历史(批式)

更强的算力:要求更快的读数据

  1. 例子:分布式任务扫描、向量化读与读时合并、统一内存格式(Apache Arrow)
  2. 应用:数据读取不应成为训练瓶颜