这是我参与「第四届青训营」笔记创作活动的的第11天
01.发展历史
数据湖三阶段: Hadoop、 Hive、 湖仓一体
1.1数据湖发展阶段- Hadoop
1)数据湖最开始的概念 ———— 分布式存储HDFS
2)使用目录来区分不同的数据集
(1) /douyin
a. /20220623
b. /20220624
(2) / toutiao
3)好处:
(1)同一公司/组织可以使用共享存储
(2)数据访问方便,灵活性高
1.1 数据湖发展阶段1 - Hadoop
- 坏处:
(1).没有记录文件的schema(包括列名、列类型),经常使用SchemaonQuery的方式
(2).难以得知数据集包含了那些文件,是通过什么样的分区组织的
(3).如果多个程序都在修改这个数据集(修改数据、修改表结构),其他程序难以配合做修改
1.2数据湖发展阶段2 - Hive
1)数据湖的演进 —— Hive Metastore
2)对数据湖中的数据集进行集中“定义”
(1)数据湖中存在了哪些数据集
(2)它们都存储在什么目录
(3)数据集的schema是什么样子的
(4)数据集有哪些分区,每个分区的目录是什么
1.2 数据湖发展阶段2 - Hive
1)如果这张hive表是静态的,没有新增写入,则所有读取方都能很便捷的使用
2)问题来了:
(1)①假设ReaderA和B都正在读取分区/20220623下的文件
(2)此时WriterB开始重写/20220623分区,一些文件被删了,-些文件增加了,一些文件还在修改中
(3)Reader A和B读到的文件可能是不同的!
3)我们需要Transaction ACID !
4)问题又来了:
(1)分区/20220623下存储了schema为date 1 userId 1 phoneNumber 的数据
(2)需要注意: Hive allows us to add column after last column only
(3)根据合规,我需要删掉phoneNumber,但是在Hive表上做不到。只好重写一张表(耗费 资源)
5)我们需要支持更多样的schema变更!
6) And more !
1.3数据湖发展阶段3 - 湖仓一体
题外话 —— 数据仓库
1)什么是数据仓库?
(1)数据仓库将数据从数据源提取和转换,加载到目的地
(2)数据仓库存储+计算不分离
(3)数据仓库严格控制写入数据的schema
1.3数据湖发展阶段3湖仓一体
1.3数据湖发展阶段3 - 湖仓一体
湖仓一体(数据湖的现状)
1)结合了数据湖和数据仓库的优势
2)将数据仓库中对于数据的严格管理直接实现到了低成本的分布式存储之上
3)Key Features:
(1)Transaction ACID
(2)Schema管理
(3)存储计算分离
(4)支持多种计算引擎和文件格式
1.4 业界三大数据湖
公司简介:
1)国外版滴滴,打车软件
2)在行程规划、拼车、顺风车等业务上数据量都很大
3)数据写入面临挑战
项目简介:
1)项目名称: Hudi
2)最初希望解决的问题:使数据写入能够更低延迟
3)开源时间:2016年
4)官网: https:/ /hudi.apache.org/
5)Github: https:/ /github.com/apache/hudi
公司简介:
- 流媒体
2)《纸牌屋》、《鱿鱼游戏》 、《怪奇物语》
- 开发工作:存储用户观看数据,推荐剧集
项目简介:
1)名称: Iceberg
2)开源时间: 2018
3)最初希望解决的问题:拜托Hive架构带来的问题
4)官网: https:/ /iceberg.apache.org/
5)Github: github.com/ apache/iceberg
公司简介:
- 起源于UCB,参与Spark的制作
2)超级独角兽
3)主打湖仓-体
项目简介:
1)项目名称: Delta Lake、
2)开源时间: 2019 (开源了 ,但没完全开源)
3)官网: delta.io/
4)Github: https:/ /github. com/ delta-io/ delta
1.5 关于“数据湖”
1)数据相关概念比较新,一直处在演进当中
2)一开始是HDFS,裸pb、txt 日志等等,叫数据湖(管不动了就叫数据沼泽)
3)后来出现了了Iceberg、Hudi、 Delta Lake了,数据湖概念就基本等于这些产品了
4)也更贴近于Lakehouse的概念
02.核心技术
设计一个简单的数据湖
需求1:
1.我想存一些数据,按照date分区schema是
2.每天都会写入新数据
3.需要使用列存格式
回答1:简单
2.1文件结构
写入数据湖时
1.按照每条数据的date进行分区
2.额外使用metadata文件记录表信息,如下图所示
需求2:我把2020-01-01的数据更新了一下,我想用新数据和老数据同时跑一下训练
回答2:可以
2.2 Time travel
要点:
1.每次写入都生成-一个新的元数据文件,记录变更
2.分区数据在Update时,不要删除旧数据,保证新旧共存
3.元数据中存储具体的文件路径,而不仅仅是分区文件夹
1.每一次写入操作,创建一个新的json文件,以递增版本号命名,记录本次新增/删 除的文件。
2.每当产生N个json,做一次聚合,记录完整的分区文件信息
3.用checkpoint记录上次做聚合的版本号
需求3:小B同学也要用这个数据湖
1.我俩要是一起写,不会有写冲突吧?
2.我俩要是同时一个人读一个人写,这个过程中不会有读写冲突吧?
回答2:不会
2.3 Transaction
Transaction 交易?事务!
ACID,是指数据库在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性。
以A给B转账10元为例:
-
Atomicity: 原子性 ———— 要么A-10 B+10,要么都不变
-
Consistency: 一致性 ———— 不可以A-10 B+5
-
Isolation: 事务隔离 一 A和C同时给B转10,B最终结果应是+20
-
Durability: 持久性 - 转账服务器重启,结果不变
数据湖中的ACID:
-
Atomicity: 原子性-本次写入要么对用户可见,要么不可见(需要设计)
-
Consistency: 一致性-输入是什么,落盘的就是什么(由计算引擎保证)
-
Isolation: 事务隔离-正确解决读写冲突和写写冲突(需要设计)
-
Durability: 持久性-落完数据后,即便服务器重启结果不变(由存储引擎保证)
2.3.1原子性
写入流程 :
- 写入parquet数据文件
2.写入json元数据文件
➢如何确保原子性? (从用户可见性入手! )
1.用户只会读取以版本号数字命名的json文件,每次都读取到最大的版本号作为数据集的现状
2.新的写入写完parquet后开始写json文件,使用hash值对json文件命名,如a2fs4hfg8ee.json
3.直到json文件内 容写入完毕,利用hdfs的renameIfAbsent能力将a2fs4hfg8ee.json替换为000006.json,到此为止commit完成,新的读取将会D以000006.json作为最新版本
➢读写冲突已经被解决,how?
1.新的写入除非已经commit,否则用户读不到
2.用户正在读的分区,被另一个写入进行了更新,数据不会进行替换,而是共存
2.3.2事务隔离
➢Update写入流程:
- 从最新的版本中,获取需要update的分区
2.乐观锁先把该写入的文件全落盘,然后进入写json阶段
3.分几种情况:
1)发现版本号和一 开始没区别, 直接写新的版本。
2)发现版本号增加了,看看新增的这些版本有没有更新我要更新的分区?
(1)没有, 直接写新的版本。
(2)有,两者都更新了同一分区,得重新update了。
需求4: 上级通知,phone 列得删了
回答4:支持
2.4 Schema Evolution
Add/Drop/ Rename 重要:
1)用户并不直接读取parquet文件本身,而是通过数据湖接口读取,如Dataset ds = simpleDataLake.read(mytable) .option(date=2020-01-01)
2)数据湖内 部会读取应该读的parquet,并在schema上做进一步处理
ID将data和metadata的列名做一一对应!
1.唯一确定的D。新增列赋予新ID。删列ID不复用。
2.写入数据时,ID也写入数据文件
3.读取数据时,用ID做映射,如果
1) Data中没有,metadata中有: ADD
2) Data中有,metadata中没有: DROP
3)Data和metadata中 都有同一ID,但是name不同: RENAME而不同
4)如果都有同一列名,而ID不同?
03.各有所长
➢用户体验
-
Schema evolution
-
Partition evolution
-
Hidden partition
-
Time Travel
-
Version Rollback
➢性能
- 快速file plan
2.更多的filter方式
➢可靠性
- ACID Transaction
➢完全开源,由Apache孵化开发
3.1.1 Well-designed Metadata Layer
1)Metadata files 定义了表结构,存储了snapshot信息,分区列信息等
2)Manifest lists存储了一个snapshot中所有manifest的信息
3)Manifests存储了一些data files 的信息
4) Data files就是具体的数据文件
3.1.2 Data File Filter
一些有助于filter 的数据被层层记录,比如:
- Manifest file 记录了每个data file 的分区范围
2.Manifest list 记录了每个manifest file 的分区范围分区可以被快速定位!可以做manifest list 级别裁剪。
- Manifest file 记录了每个data file 每一列的最大值,最小值可以通过其他的列(UserId) 做data file 级别裁剪。
3.1.3 Hidden Partition
➢传统的分区方式:
1)数据中包含了date列,则按照date分区;如果希望按照hour分区,则需要新增hour列
➢Iceberg的分区方式:
1)数据中包含timestamp列,设置好partition transform方式
(1)设置为date时,iceberg 帮你转化为date分区
(2)设置为hour时,iceberg 帮你转化为hour分区
(3)Iceberg记录了这层转化关系,并且按你的需要进行partition evolution
3.2 Hudi
Hadoop Upsert Delete and Incremental
Hudi工作重点:
-
Timeline service: Hudi 管理transaction的方式
-
Hudi Table Type: Copy on Write / Merge on Read
3.高效的Upserts: update or insert
4.索引表:快速定位一条数据的位置
-
Streaming Ingestion Service
-
完全开源,由Apache孵化
3.2.1 Timeline Serivce & Upsert & Incremental
1) Timeline Service: 记录的信息类似于metadata
2) Upsert:每-条样本都有一个主键PK,当Upsert一条数据时,如果现有的数据中有这个PK,则update这条数据。否则直接insert这条数据
3) Incremental: 某个时间点后新增的数据
3.2.2 Copy on Write
3.2.3 Merge On Read
##3.3 Delta Lake工作重点
1.ACID Transaction
2.Schema校验(不是evolution)
3.流批一体
4.Time Travel
5.Upsert/Delete
6.Z-Order优化
7.只开源了一部分,由Databricks自己主导开发,Z-order 等优化的实现未开源
3.3.1流批一体
04.总结场景
4.1回顾:三个数据湖的异同
4.2三个数据湖的热度
4.3技术选型
短期来看:每个项目都有于自己的功能:
1)如果强需求Upserts,也许Hudi是最好的选择
2)如果希望可扩展性强,那么设计优良的Iceberg是最好的选择
3)如果希望用上Z-Order等优化,那么掏钱买Databricks的企业版Delta是不二之选
长期来看:数据湖取代Hive, 成为HDFS上的表格式标准是必然的,在选择之前问自己四个问题:
1.我需要的 feature在哪个数据湖上是最稳定的
2.哪一个数据湖能够用最简单的接入方式(SQL) 用上最完善的功能
3.哪一个数据湖有计算引擎侧的支持和社区的支持
4.哪一个数据湖的版本管理做的最好,最鲁棒
4.3字节跳动数据湖场景举例 - Feature Store
Feature Store在字节的诞生
➢初心
1.instance pb样本读放大、不能列裁剪=>很难落许多特征进样本
2.2. instance pb样本写放大、copy-on-write =>很难做特征回溯
➢收益
1.支持落原始特征做特征调研
2.解决读放大、支持列裁剪
3.解决写放大、支持merge-on-read
4.支持特征 schema校验
5.列式存储优化存储空间
6.Arrow 格式优化序列化开销
Feature Store及其平台
● 基于Apache Iceberg 做了大量定制与优化
● 站内存储约220 PB训练样本
(1)最大单表30PB,15K 个特征
(2)相比instance pb样本能节省约30% ~ 50%空间
● 平台端到端体验完整、用户使用成本低
(1)控制面板、 特征监控、数据维护(样本回溯、TTL、 脏数据处理等)
● 对特征回溯及特征调研场景做了专门强化
(1)支持Update语义操作和数据分支
4.3字节跳动数据湖场景举例
➢批流一体:近实时的入与数据Readiness)
1.例子:消息队列直接入湖、流式Upsert
2.应用:能同时训最新(流式)和历史(批式)
➢更强的算力:要求更快的读数据
- 例子:分布式任务扫描、向量化读与读时合并、统一~内存格式(Apache Arrow)
2.应用:数据读取不应成为训练瓶颈