Hudi 元数据表详解

2024-05-23 warehouse hudi

简介

这里指的 Hudi 自身的元数据,同时为了扩展性,设计时有如下的要求:

  • 可扩展的元数据,独立于计算及查询引擎,支持不同类型的索引。
  • 事务性,元数据和数据表保持实时同步。
  • 查询速度,常见的查询类型有 PointRangePrefix 几种。

通过 hoodie.metadata.enable 参数使能后,会生成 .hoodie/metadata/ 的内部 MOR 表,以此提升源表的读写性能,主要包含如下几个分区目录:

  • files 源表各个分区内所有的文件列表,降低 list files 操作的开销。
  • column_stats 记录各个分区所有文件的统计信息,主要是每个文件各个列的最大值、最小值、记录数、空值数等。

对于 BaseLog 文件均采用 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 ListingFile Listing 涉及大量 IO 操作,导致耗时较多。

湖格式和传统表结构不同,有其特有的元数据,例如时间线和多版本的文件,通过元数据表使得读写获得更好的性能,可以减少文件系统的操作。

默认相关的元数据保存在内部的 MOR 表中,详细设计参考 RFC-15 中的相关介绍。

column_stats

通过 hoodie.metadata.index.column.stats.enable 参数开启,默认会统计所有列,可以通过 hoodie.metadata.index.column.stats.column.list 参数指定列,统计信息会在提交的文件写入前统计完成,提交时完成更新。