Hudi系列3:Hudi核心概念
Hudi架构
image.png一. 时间轴(TimeLine)
1.1 时间轴(TimeLine)概念
Hudi的核心是维护在不同时刻(Instant)在表上执行的所有操作的时间轴,提供表的即时视图,同时还有效地支持按时间顺序检索数据
image.png
1.2 Hudi的时间线由组成
Instant action:
在表上执行的操作类型
Instant time:
即时时间,通常是一个时间戳,它按照action的开始时间单调递增
State:
时刻的当前状态
1.3 时间线上的Instant action操作类型
hudi保证在时间线上的操作都是基于即时时间的,两者的时间保持一致并且是原子性的
-
commits: 表示将一批数据原子写入表中
-
cleans: 清除表中不在需要的旧版本文件的后台活动。
-
delta_commit:增量提交是指将一批数据原子性写入MergeOnRead类型的表中,其中部分或者所有数据可以写入增量日志中。
-
compaction: 协调hudi中差异数据结构的后台活动,例如:将更新从基于行的日志文件变成列格式。在内部,压缩的表现为时间轴上的特殊提交。
-
rollback:表示提交操作不成功且已经回滚,会删除在写入过程中产生的数据
-
savepoint:将某些文件标记为“已保存”,以便清理程序时不会被清楚。在需要数据恢复的情况下,有助于将数据集还原到时间轴上某个点。
1.4 时间线上State状态类型
任何给定的瞬间都可以处于以下状态之一
-
requested:表示一个动作已被安排,但尚未启动
-
inflight:表是当前正在执行操作
-
completed:表是在时间线上完成了操作
1.5 时间线官网实例
image.png上图中采用时间(小时)作为分区字段,从 10:00 开始陆续产生各种 commits,10:20 来了一条 9:00 的数据,该数据仍然可以落到 9:00 对应的分区,通过 timeline 直接消费 10:00 之后的增量更新(只消费有新 commits的 group),那么这条延迟的数据仍然可以被消费到,(没有说明白它是怎么处理延迟数据的
)
二. 文件布局
Hudi将一个表映射为如下文件结构:
image.png
-
Hudi将HDFS上的数据集组织到基本路径(HoodieWriteConfig.BASEPATHPROP)下的目录结构中。
-
数据集分为多个分区(DataSourceOptions.PARTITIONPATHFIELDOPT_KEY),这些分区与Hive表非常相似,是包含该分区的数据文件的文件夹。
-
数据集分为多个分区(DataSourceOptions.PARTITIONPATHFIELDOPT_KEY),这些分区与Hive表非常相似,是包含该分区的数据文件的文件夹。
-
Hudi将数据表组织成分布式文件基本路径(basepath)下的目录结构。
-
表被划分为多个分区,这些分区是包含该分区的数据文件的文件夹,非常类似于Hive表
-
在每个分区中,文件被组织成文件组,由文件ID唯一标识
-
每个文件组包含几个文件片(FileSlice)
-
每个文件包含:
5.1) 一个基本文件(.parquet): 在某个commit/compaction 即时时间(instant time)生成的(MOR可能没有)
5.2) 多个日志文件(.log*),这些日志文件包含自生成基本文件以来对基本文件的插入/更新(COW没有). -
Hudi采用了多版本并发控制(Multiversion Concurrency Control,MVCC)
6.1) compaction 操作: 合并日志和基本文件以产生新的文件片
6.2) clean操作: 清楚不使用的/旧的文件以回收文件系统上的空间 -
Hudi的base file(parquet 文件)在 footer 的 meta 去记录了 record key组成的BloomFilter,用于在flie based index 的实现中实现高效率的 key contains 检测。 只有不在 BloomFilter的key才需要扫描整个文件消灭假阳。
-
Hudi的log(avro 文件)是自己编码的,通过积攒数据 buffer 以LogBlock为单位写出,每个LogBlock包含 magic number、size、content、footer等信息,用于数据读、校验和过滤。
三. 索引
3.1 简介
Hudi 通过索引机制将给定的 hoodie 键(记录键 + 分区路径)一致地映射到文件 id,从而提供高效的 upsert。记录键和文件组/文件 id 之间的这种映射,一旦记录的第一个版本被写入文件,就永远不会改变。简而言之,映射文件组包含一组记录的所有版本(类似git)。
3.2 对比Hive没有索引的区别
对于Copy-On-Write 表,这可以实现快速 upsert/delete 操作,避免需要连接整个数据集以确定要重写哪些文件。对于Merge-On-Read 表,这种设计允许 Hudi 绑定任何给定基本文件需要合并的记录数量。具体来说,给定的基本文件只需要针对作为该基本文件一部分的记录的更新进行合并。相反,没有索引组件的设计 Hive ACID需要将所有基本文件与所有传入的更新/删除记录合并
image.png
3.3 Hudi索引类型
image.png3.4 全局索引与非全局索引
Bloom 和 simple index 都有全局选项,Base 索引本质上是一个全局索引
hoodie.index.type=GLOBAL_BLOOM
hoodie.index.type=GLOBAL_SIMPLE
-
全局索引
:在全表的所有分区范围下强制要求键保持唯一,即确保对给定的键有且只有一个对应的记录。 -
非全局索引
:仅在表的某一个分区内强制要求键保持唯一,它依靠写入器为同一个记录的更删提供一致的分区路。
四. 表类型
4.1 COW:(Copy on Write)写时复制表
4.1.1 概念
Copy on Write表中的文件切片仅包含基本列文件,并且每次提交都会生成新版本的基本文件。换句话说,每次提交操作都会被压缩,以便存储列式数据,因此Write Amplification写入放大非常高(即只有一个字节的数据被提交修改,我们也需要先copy改值所在的最小单位块复制到内存整体修改再写会去),而读取数据成本则没有增加,所以这种表适合于做分析工作,读取密集型的操作。
4.1.2 COW工作原理
image.png当数据被写入,对现有文件组的更新会为该文件组生成一个带有提交即时间标记的新切片,而插入分配一个新文件组并写入该文件组第一个切片。这些切片和提交即时时间在上图用同一颜色标识。针对图上右侧sql查询,首先检查时间轴上的最新提交并过滤掉之前的旧数据(根据时间查询最新数据),如上图所示粉色数据在10:10被提交,第一次查询是在10:10之前,所以不会出现粉色数据,第二次查询时间在10:10之后,可以查询到粉色数据(以被提交的数据)。
4.1.3 COW表对表的管理方式改进点
-
在原有文件上进行自动更新数据,而不是重新刷新整个表/分区
-
能够只读取修改部分的数据,而不是浪费查询无效数据
-
严格控制文件大小来保证查询性能(小文件会显著降低查询性能)
4.2 MOR:(Merge on Read)读时复制表
4.2.1 概念
Merge on Read表使用列式存储(parquet)+行式文件(arvo)存储数据,它仍然支持只查询文件切片中的基本列(parquet)来对表进行查询优化。用户每次对表文件的upsert操作都会以增量写入delta log(avro),增量日志会对应每个文件最新的ID来帮助用户完成快照查询。因此这种表类型,能够智能平衡读取和写放大(wa),提供近乎实时的数据。这种表最重要的是合并压缩,它用来选择将对应delta log数据合并压缩到表的基本文件中,来保持查询时的性能(较大的增量日志文件会影响合并时间和查询时间)(通俗说就是你修改新增的值存在一个avro格式文件中,等你要查询的时候就好去和原有的值进行合并操作返回唯一值,不过MOR表会定期自动合并)
Merge on Read 读时合并
第一批数据,没有Parquet文件,写入到log文件
后期会compaction,执行合并的时候,将Parquet+log合并为新的
Parquet。(有点类似关系型数据库的WAL
)
4.2.2 MOR表工作原理
image.png-
如上图所示,可以做到每一分钟提交一次写入操作
-
查询表的方式有两种,Read Optimized query和Snapshot query,取决于我们选择是要查询性能还是数据最新
-
如上图所示,Read Optimized query查询不到10:05之后的数据(查询不到增量日志里的数据,没有合并到base文件),而Snapshot query则可以查询到全量数据(基本列数据+行式的增量日志数据)
4.3 总结了两种表类型之间的权衡
image.png五. 查询类型
Hudi支持如下三种查询类型:
5.1 Snapshot Queries
image.png5.2 Incremental Queries
增量查询,可以查询给定 commit/delta commit 即时操作以来新写入的数据。 有效的提供变更流来启用增量数据管道。
5.3 Read Optimized Query
读优化查询,可查看给定的 commit、compact 即时操作的表的最新的快照。 仅将最新文件片的基本/列文件暴露给查询,并保证与非Hudi表相同的列查询性能。