HUDI-0.11.0 BUCKET index on Flink 新特性试用
1.背景
在0.10.1版本下,用默认的index(FLINK_STATE),在upsert的模式下,几十亿级别的数据更新会很消耗内存以及ckp时过长,因此切换到0.11.0的BUCKET索引;
仅对于当前环境:flink1.13.2 + hudi 0.11.0-(master 2022.04.11) + cow + hdfs;
关键配置项:'index.type' = 'BUCKET', 'hoodie.bucket.index.num.buckets' = '256'
关键词 hudi cow FLINK BUCKET FLINK_STATE
2.BUCKET与FLINK_STATE的区别
FLINK_STATE: 简单的说,hudi的upsert模式需要指定主键组,更新时是按照主键进行更新的,而数据是存在于hdfs文件上的,那么主键与文件名的映射就是必须的 => 依托Flink特性,存在state里面;因此程序第一次加载hudi表的历史数据时,需要设置 'index.bootstrap.enabled' = 'true' 来加载历史数据到state里,更新可以跨分区;
BUCKET:简单的说,就是”基于文件的分桶“,比如设置主键为id,桶个数256('hoodie.bucket.index.num.buckets' = '256'),那么计算bucket序号的方法就是(id.hashCode() & Integer.MAX_VALUE) % 256;
而且一旦设置,桶(buckets)的个数是不能变的,对应文件个数是不变的 => 预估数据量来保证合理的文件数量与大小,减少小文件或过度写放大(因为文件个数不变,单个文件大小会一直增大);优点:无内存(仅指flink|Managed Memory)占用,缺点:文件IO带来cpu压力会升高;
BUCKET是基于单个文件的设置,因此不能跨分区;
tips:bucket个数预估可以使用离线导数据看hdfs文件大小来预估;
总结:FLINK_STATE占内存,初始化加载历史数据慢,可跨分区;BUCKET占磁盘,不可跨分区,省内存;
3.相关配置
flink实时流配置
'connector' = 'hudi',
'path' = 'hdfs://path/',
'index.type' = 'BUCKET', -- bucket索引
'hoodie.parquet.compression.codec'= 'snappy',
'table.type' = 'COPY_ON_WRITE',
'write.operation' = 'upsert',
'write.task.max.size' = '2048',
'write.precombine' = 'true',
'write.precombine.field' = 'update_time',
'write.tasks' = '6',
'write.bucket_assign.tasks' = '6',
'hoodie.bucket.index.hash.field' = 'id', -- 主键
'hoodie.bucket.index.num.buckets' = '256', -- 桶个数
'hive_sync.enable'='true',
'hive_sync.table'='TABLE_NAME',
'hive_sync.db'='DB_NAME',
'hive_sync.mode' = 'hms',
'hive_sync.metastore.uris' = 'thrift://HOST:9083',
'hive_sync.skip_ro_suffix' = 'true',
'write.insert.cluster' = 'true',
'write.ignore.failed' = 'true',
'clean.async.enabled' = 'true',
'clean.retain_commits' = '3',
'hoodie.cleaner.commits.retained' = '3',
'hoodie.keep.min.commits' = '4',
'hoodie.keep.max.commits' = '8'
Flink离线导入数据配置
'connector' = 'hudi',
'path' = 'hdfs://PATH',
'hoodie.parquet.compression.codec'= 'snappy',
'index.type' = 'BUCKET',
'table.type' = 'COPY_ON_WRITE',
'write.operation' = 'bulk_insert',
'write.tasks' = '2',
'hoodie.bucket.index.num.buckets' = '256',
'hoodie.bucket.index.hash.field' = 'id'
离线导入完成后,观察hdfs文件前八位为数字,例如00000000-,00000255-,即设置成功,然后可直接接入实时数据;
注意:从hive导数据到hudi,可以调整一下hive source的并行度:
tableConfig.setInteger(HiveOptions.TABLE_EXEC_HIVE_INFER_SOURCE_PARALLELISM_MAX, source_parallelism_max)
4.性能小结
实时情况:基于目前的数据量,单文件(80M)操作在100ms左右:eg: block read in memory in 171 ms. row count = 617384,十几张表每次ckp三四分钟左右,对于我们十几分钟的ckp来说可以接受;
离线导数据情况:对于亿级别数据的离线导入,资源不算大,十几分钟就导入完成了;
注:如果ckp设置太小,cow表情况下,频繁操作bucket文件,会对集群cpu load产生压力;