一、Hudi整体架构
Hudi表的三个主要特点:
1)、
timeline metadata 有序的时间轴元数据,类似于数据库事务日志。
2)、
分层布局的数据文件:base file + delta log file(主要⾯向对base file的update&delete);
3)、
index 索引(多种实现方式):默认是file group级别的bloomfilter,可定制
二、写⼊存储类型(表类型)
1、COW:(Copy on Write)写时复制表
1)、
概念
Copy on Write表中的文件切片仅包含基本列文件,并且每次提交都会生成新版本的基本文件。换句话说,每次提交操作都会被压缩,以便存储列式数据,因此Write Amplification写入放大非常高(即只有一个字节的数据被提交修改,我们也需要先copy改值所在的最小单位块复制到内存整体修改再写会去),而读取数据成本则没有增加,所以这种表适合于做分析工作,读取密集型的操作。
2)、
COW表工作原理
当数据被写入,对现有文件组的更新会为该文件组生成一个带有提交即时间标记的新切片,而插入分配一个新文件组并写入该文件组第一个切片。这些切片和提交即时时间在上图用同一颜色标识。针对图上右侧sql查询,首先检查时间轴上的最新提交并过滤掉之前的旧数据(根据时间查询最新数据),如上图所示粉色数据在10:10被提交,第一次查询是在10:10之前,所以不会出现粉色数据,第二次查询时间在10:10之后,可以查询到粉色数据(以被提交的数据)。
3)、
COW表对表的管理方式改进点
(1)、在原有文件上进行自动更新数据,而不是重新刷新整个表/分区
(2)、能够只读取修改部分的数据,而不是浪费查询无效数据
(3)、严格控制文件大小来保证查询性能(小文件会显著降低查询性能)
2、MOR:(Merge on Read)读时复制表
1)、
概念
Merge on Read表使用列式存储(parquet)+行式文件(arvo)存储数据,它仍然支持只查询文件切片中的基本列(parquet)来对表进行查询优化。用户每次对表文件的upsert操作都会以增量写入delta log(avro),增量日志会对应每个文件最新的ID来帮助用户完成快照查询。因此这种表类型,能够智能平衡读取和写放大(wa),提供近乎实时的数据。这种表最重要的是合并压缩,它用来选择将对应delta log数据合并压缩到表的基本文件中,来保持查询时的性能(较大的增量日志文件会影响合并时间和查询时间)(通俗说就是你修改新增的值存在一个avro格式文件中,等你要查询的时候就好去和原有的值进行合并操作返回唯一值,不过MOR表会定期自动合并)
2)、
MOR表工作原理
(1)、如上图所示,可以做到每一分钟提交一次写入操作,
(2)、查询表的方式有两种,Read Optimized query和Snapshot query,取决于我们选择是要查询性能还是数据最新
(3)、如上图所示,Read Optimized query查询不到10:05之后的数据(查询不到增量日志里的数据,没有合并到base文件),而Snapshot query则可以查询到全量数据(基本列数据+行式的增量日志数据)
3)、总结了两种表类型之间的权衡
权衡 |
CopyOnWrite |
MergeOnRead |
数据延迟 |
高 |
低 |
查询延迟 |
低 |
高 |
Update(I/O) 更新成本 |
高(重写整个Parquet文件) |
低(追加到增量日志) |
Parquet File Size |
低(更新成本I/O高) |
较大(低更新成本) |
Write Amplification(WA写入放大) |
大 |
低(取决于压缩策略) |
三、Hudi数据文件组织
1、表存储路径目录结构
如下图是hudi_trips_cow表在HDFS的存储组织(
hudi表数据可以存储在hdfs分布式系统,也可以存储在本地
)
1)、
.hoodie 文件
:由于CRUD的零散性,每一次的操作都会生成一个文件,这些小文件越来越多后,会严重影响HDFS的性能,Hudi设计了一套文件合并机制。
.hoodie文件夹中存放了对应的文件合并操作相关的日志文件
2)、amricas和asia相关的路径是实际的数据文件,按分区存储,分区的路径key是可以指定的,
数据文件使用Parquet文件格式存储
,其中包含一个
metadata元数据文件
和数据文件
3)、.hoodie_partition_metadata 元数据
里面保存了commit的时间和分区深度信息
2、数据存储结构
1)、
介绍
Hudi数据集的目录结构与Hive表示非常相似,
一份数据集对应这一个根目录
。数据集被
打散为多个分区,分区字段以文件夹形式存在,该文件夹包含该分区的所有文件
2)、
特点
在每个分区内,文件被组织为文件组,由文件id充当唯一标识。每个文件组包含多个文件切片,其中每个切片包含在某个即时时间的提交/压缩生成的
基本列文件(.parquet)以及一组日志文件(.log)
,该文件包含自生成基本文件以来对基本文件的插入/更新
3)、
Huid数据整体流转
四、时间线Timeline
1、介绍
Hudi的核心是维护在不同时刻(Instant)在表上执行的所有操作的时间轴,提供表的即时视图,同时还有效地支持按时间顺序检索数据
2、Hudi的时间线由组成
1)、
Instant action
:
在表上执行的操作类型
2)、
Instant time
: 即时时间,通常是一个时间戳,它按照action的开始时间单调递增
3)、
State
: 时刻的当前状态
3、时间线上的Instant action操作类型
(
hudi保证在时间线上的操作都是基于即时时间的,两者的时间保持一致并且是原子性的
)
1)、
commits
: 表示将一批数据原子写入表中
2)、
cleans
: 清除表中不在需要的旧版本文件的后台活动。
3)、
delta_commit
:增量提交是指将一批数据原子性写入MergeOnRead类型的表中,其中部分或者所有数据可以写入增量日志中。
4)、
compaction
: 协调hudi中差异数据结构的后台活动,例如:将更新从基于行的日志文件变成列格式。在内部,压缩的表现为时间轴上的特殊提交。
5)、
rollback
:表示提交操作不成功且已经回滚,会删除在写入过程中产生的数据
6)、
savepoint
:将某些文件标记为“已保存”,以便清理程序时不会被清楚。在需要数据恢复的情况下,有助于将数据集还原到时间轴上某个点。
4、时间线上State状态类型
(
任何给定的瞬间都可以处于以下状态之一
)
1)、
requested
:表示一个动作已被安排,但尚未启动
2)、
inflight
:表是当前正在执行操作
3)、
completed
:表是在时间线上完成了操作
5、时间线官网实例
上图中采用时间(小时)作为分区字段,从 10:00 开始陆续产生各种 commits,10:20 来了一条 9:00 的数据,该数据仍然可以落到 9:00 对应的分区,通过 timeline 直接消费 10:00 之后的增量更新(只消费有新 commits的 group),那么这条延迟的数据仍然可以被消费到,(
没有说明白它是怎么处理延迟数据的
)
五、Index(索引)
1、简介
Hudi 通过索引机制将给定的 hoodie 键(记录键 + 分区路径)一致地映射到文件 id,从而提供高效的 upsert。记录键和文件组/文件 id 之间的这种映射,一旦记录的第一个版本被写入文件,就永远不会改变。简而言之,映射文件组包含一组记录的所有版本(
类似git
)。
2、对比Hive没有索引的区别
对于
Copy-On-Write 表
,这可以实现快速 upsert/delete 操作,
避免需要连接整个数据集以确定要重写哪些文件
。对于
Merge-On-Read 表
,这种设计允许 Hudi 绑定任何给定基本文件需要合并的记录数量。具体来说,给
定的基本文件只需要针对作为该基本文件一部分的记录的更新进行合并
。相反,没有索引组件的设计
Hive ACID
需要将所有基本文件与所有传入的更新/删除记录合并
3、Hudi索引类型
1)、
Bloom Index(默认)
:使用由记录键构建的Bloom过滤器,还可以选择使用记录键范围修剪候选文件。
2)、
Simple Index
:针对从存储表中提取的键执行传入更新/删除记录的连接。
3)、
HBase Index
:管理外部 Apache HBase 表中的索引映射。
4)、
自定义实现
:您可以扩展此公共 API 以实现自定义索引。
注意:
可以使用hoodie.index.type配置选项选择索引
4、全局索引与非全局索引
Bloom 和 simple index 都有全局选项,Base 索引本质上是一个全局索引
hoodie.index.type=GLOBAL_BLOOM
hoodie.index.type=GLOBAL_SIMPLE
1)、
全局索引
:在全表的所有分区范围下强制要求键保持唯一,即确保对给定的键有且只有一个对应的记录。
2)、
非全局索引
:仅在表的某一个分区内强制要求键保持唯一,它依靠写入器为同一个记录的更删提供一致的分区路。