简介
这里指的 Hudi 自身的元数据,同时为了扩展性,设计时有如下的要求:
- 可扩展的元数据,独立于计算及查询引擎,支持不同类型的索引。
- 事务性,元数据和数据表保持实时同步。
- 查询速度,常见的查询类型有
Point
、Range
、Prefix
几种。
通过 hoodie.metadata.enable
参数使能后,会生成 .hoodie/metadata/
的内部 MOR 表,以此提升源表的读写性能,主要包含如下几个分区目录:
files
源表各个分区内所有的文件列表,降低list files
操作的开销。column_stats
记录各个分区所有文件的统计信息,主要是每个文件各个列的最大值、最小值、记录数、空值数等。
对于 Base
和 Log
文件均采用 HFile
格式,主要是为了提高点查能力。其内部同样采用 Hudi
格式,所以,可以与查询普通 Hudi
表类似的方式进行查询:
// 这里返回的是Dataset类型
val mdt = spark.read.format("org.apache.hudi").load("hdfs://hacluster/tmp/hudi_idx_table/.hoodie/metadata")
mdt.show()
mdt.createOrReplaceTempView("hudi_mdt_table")
spark.sql("select * from hudi_mdt_table").show()
spark.sql("select key, type, filesystemMetadata from hudi_mdt_table WHERE filesystemMetadata is not null").show(false)
spark.sql("select key, type, ColumnStatsMetadata from hudi_mdt_table WHERE ColumnStatsMetadata is not null").show(false)
spark.sql("select key, type, BloomFilterMetadata from hudi_mdt_table WHERE BloomFilterMetadata is not null").show(false)
spark.sql("select key, type, recordIndexMetadata from hudi_mdt_table WHERE recordIndexMetadata is not null").show(false)
实际上 HFile
采用的是 KV
方式保存数据,所以查询时关键的就是 Key
的计算,不同的分区使用方式略有区别。以 ColumnStats
为例,生成方式详见 HoodieMetadataPayload.createColumnStatsRecords()
中的 HoodieKey
实现。
files
Hudi 每次操作数据都会新增时间线,查询时需要通过时间线元数据获得在该时间点上的有效分区或文件,导致 Partition Listing
和 File Listing
涉及大量 IO 操作,导致耗时较多。
湖格式和传统表结构不同,有其特有的元数据,例如时间线和多版本的文件,通过元数据表使得读写获得更好的性能,可以减少文件系统的操作。
默认相关的元数据保存在内部的 MOR 表中,详细设计参考 RFC-15 中的相关介绍。
column_stats
通过 hoodie.metadata.index.column.stats.enable
参数开启,默认会统计所有列,可以通过 hoodie.metadata.index.column.stats.column.list
参数指定列,统计信息会在提交的文件写入前统计完成,提交时完成更新。