写在开始
- 本篇带大家分析一下Hudi中MOR表的文件结构
- 刚开始看Hudi一周,有什么不对的地方欢迎大家指出
事前准备
建表
-- 先准备一张Hudi MOR表
CREATE TABLE hudi_test_dijie(
id bigint,
dt string,
ts TIMESTAMP(3),
PRIMARY KEY(id) NOT ENFORCED
)
PARTITIONED BY (`dt`)
WITH (
'connector'= 'hudi',
'path'= 'hdfs:///hudi/hudi_test_dijie',
'table.type'= 'MERGE_ON_READ',
'read.streaming.enabled'= 'true',
-- 'read.streaming.start-commit'= '20210401134557',
'read.streaming.check-interval'= '4'
);
-- 再准备一张有数据的Kafka表
CREATE TABLE kafka_test (
id bigint,
dt string,
ts as localtimestamp
) WITH (
'connector' = 'kafka',
'topic' = 'a_dijie_test',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'scan.startup.mode' = 'earliest-offset',
'format' = 'json'
);
插入数据
-- 我的任务开了checkpoint,并且周期是一秒;写入Hudi请开启checkpoint
-- 我分别在15点、18点、19点执行了这段SQL,并且在19点执行的时候,使用了MiniBatch模式
-- set table.exec.mini-batch.enabled=true;
-- set table.exec.mini-batch.allow-latency=5s;
-- set table.exec.mini-batch.size=5000;
set table.dynamic-table-options.enabled=true;
insert into hudi_test_dijie select * from kafka_test /*+ options ('properties.group.id' = 'test_d') */;
数据下载
# 使用Hdfs 命令下载目录下所有数据
# 注意这里的目录用的是建Hudi表时指定的`path`,而非{DAWEHOUSE}/databases/table
hdfs dfs -get /hudi/hudi_test_dijie
工具下载
以下适用于Mac电脑
# 用于查看avro文件
brew install avro-tools
# 用于查看parquet文件
brew install parquet-tools
非Mac电脑可以直接下载对应的Jar包,下载路径
- avro:http://archive.apache.org/dist/avro/avro-1.9.2/java/avro-tools-1.9.2.jar
- parquet:http://logservice-resource.oss-cn-shanghai.aliyuncs.com/tools/parquet-tools-1.6.0rc3-SNAPSHOT.jar?spm=5176.doc52798.2.7.H3s2kL&file=parquet-tools-1.6.0rc3-SNAPSHOT.jar
开始分析
先看看表下面都有什么文件
目录下有两个文件夹,分别是.hoodie
和2021-05-01
首先.hoodie
目录对应着表的元数据信息,包括表的版本管理(Timeline)、归档目录(存放过时的instant也就是版本)、回滚记录(.rollback
)文件等等;因为我们也没有手动触发Hudi自身的savepoint
操作,没有savepoint
文件,一会儿会简单说一下这个文件的产生和作用
2021-05-01
对应着分区路径,因为我Kafka中dt
字段的数据都是指定为2021-05-01
,所以只有该天的分区,分区里面存放着Base File(*.parquet)和Log File(*.log.*)以及分区信息
接下来,我们分成表元数据
和表数据
两部分来进行分析
表元数据
之前介绍Hudi的时候和大家说过,Hudi有个Timeline
的概念,它包含了在不同Instant
时间所有对表的操作,一个Instant
由以下3种东西构成
- Instant action :对表执行的操作类型
- Instant time :Instant时间通常是一个时间戳(比如:20210501182722),该时间戳按操作开始时间的顺序单调增加
- state :Instant的状态
Hudi保证在时间轴上执行的操作的原子性和基于Instant时间的时间轴一致性。
执行的关键操作包括
-
COMMITS :一次提交表示将一组记录原子写入到表中。
-
CLEANS :删除表中不再需要的旧文件版本的后台活动。
-
DELTA_COMMIT :增量提交是指将一批记录原子写入到MOR表中,其中部分或全部数据都将只写入到日志中。
-
COMPACTION :合并日志、小文件的后台操作。合并其实是
Timeline
上的特殊提交。 -
ROLLBACK :表示
COMMITS
/DELTA_COMMIT
不成功并且进行回滚,删除在写入过程中产生的所有不完整的文件。 -
SAVEPOINT :由
hudi-CLI
手动触发,对Instant
进行备份,当后续出现提交错误时,便可ROLLBACK
至指定SAVEPOINT
任何给定的Instant都可以处于以下状态之一
-
REQUESTED :表示已调度但尚未开始的操作。
-
INFLIGHT :表示当前正在执行该操作。
-
COMPLETED :表示在
Timeline
上完成了该操作。
以上是我对官网首页文档的翻译,认真看完了这些,再来看.hoodie
下的文件会更清晰
我们按照时间顺序,依次打开有文件内容的文件
首先我们看一下./20210501150108.deltacommit
文件中的内容
{
"partitionToWriteStats": {
"2021-05-01": [
{
"fileId": "817c5634-926f-41e9-a4b1-5c52ae4ce81b",
"path": "2021-05-01/.817c5634-926f-41e9-a4b1-5c52ae4ce81b_20210501150108.log.1_1-4-0",
"prevCommit": "20210501150108",
"numWrites": 150159,
"numDeletes": 0,
"numUpdateWrites": 72544,
"numInserts": 77615,
"totalWriteBytes": 18470596,
"totalWriteErrors": 0,
"tempPath": null,
"partitionPath": "2021-05-01",
"totalLogRecords": 0,
"totalLogFilesCompacted": 0,
"totalLogSizeCompacted": 0,
"totalUpdatedRecordsCompacted": 0