Hudi 不可解析分区路径(Non-Extractable Partition Path)测试数据构建与 Trino 查询实践

发布时间:2026/10/6 18:36:57
Hudi 不可解析分区路径(Non-Extractable Partition Path)测试数据构建与 Trino 查询实践
数据湖湖仓一体大数据数据存储【免费下载链接】hudiUpserts, Deletes And Incremental Processing on Big Data.项目地址https://gitcode.com/gh_mirrors/hud/hudi点击查看免费下载Hudi 的分区路径Partition Path通常由分区列值经 PartitionValueExtractor 解析生成但当分区值自身包含路径分隔符如日期被格式化为yyyy/MM/dd、导致按斜杠切分后的段数与分区列数量不匹配时标准分区提取器将无法解析。本文以 hudi_non_extractable_partition_path.md 为骨架完整还原这类 COW 测试表的构建脚本并结合hudi-trino模块的测试初始化器与源码说明这类“不可解析分区路径”的表如何被构造、注册到元数据并在 Trino 中通过 Hive 分区名正常查询与过滤。读完本文你将掌握如何手工造出这类边界场景的表、其底层解析失败的根因以及 Trino Hudi 连接器依赖 Hive Metastore 分区元数据完成读取的完整链路。一、场景背景什么样的分区路径“不可解析”在 Hudi 中写数据时通过hoodie.datasource.write.partitionpath.field指定分区字段默认的 [SimpleKeyGenerator / 分区提取逻辑] 会把分区字段的值按规则拼成目录路径。标准的行为是单分区字段路径段数等于 1值与目录一一对应多分区字段按/分隔后段数必须与分区字段数量严格一致。一旦分区字段值本身含有/例如日期格式化成了yyyy/MM/dd目录结构就会变为2018/10/05/10——两个分区字段dt、hh却对应 4 个路径段。标准 Hudi 分区提取器在把路径段映射回dt、hh两个分区列时无法确定哪一段属于哪个字段因而称其为 “non-extractable partition path”。该场景的权威注释位于测试初始化器 ResourceHudiTablesInitializer.javaThe partition spec has 2 partition keys (dt, hh). However, the corresponding value string has 4 segments when split by slashes (year, month, day, hour). Standard Hudi partition extractors will not be able to correctly parse this mapping, since they expect the number of slash-separated values to match the number of partition keys if theres more than one partition field.二、测试表结构与创建脚本原文档定义了这张测试表的核心约束COWCopy-On-Write表只有基础文件base file无日志文件分区字段dt,hh的组合值在 Hudi 分区提取器看来“不可解析”目的是验证查询引擎在分区元数据而非从路径反推分区值可用时依然能正确读取和过滤。完整的 Spark Scala 创建脚本来自 hudi_non_extractable_partition_path.mdimport org.apache.spark.sql.types._ import org.apache.spark.sql.functions._ import org.apache.spark.sql.SaveMode import org.apache.spark.sql.Row val schema StructType(Seq( StructField(id, LongType, nullable false), StructField(name, StringType, nullable true), StructField(ts, LongType, nullable true), StructField(dt, StringType, nullable false), StructField(hh, StringType, nullable false) )) val data Seq( Row(1L, Alice, 1723272000000L, 2018-10-05, 10), Row(2L, Bob, 1723358400000L, 2018-10-05, 10), Row(3L, Charlie, 1723444800000L, 2018-10-06, 5), Row(4L, David, 1723531200000L, 2018-10-07, 5) ) val dfRaw spark.createDataFrame( spark.sparkContext.parallelize(data), schema ) val df dfRaw.withColumn(dt, date_format(to_date($dt, yyyy-MM-dd), yyyy/MM/dd)) var basePath file:///tmp/hudi_non_extractable_partition_path df.write .format(hudi) .option(hoodie.table.name, hudi_non_extractable_partition_path) .option(hoodie.datasource.write.recordkey.field, id) .option(hoodie.datasource.write.partitionpath.field, dt,hh) .option(hoodie.datasource.write.hive_style_partitioning, false) .option(hoodie.datasource.write.operation, insert) .mode(SaveMode.Overwrite) .save(basePath)脚本中的几个关键点要素取值作用记录键id由hoodie.datasource.write.recordkey.field指定Hudi 据此生成_hoodie_record_key分区字段dt,hh多分区字段同时是“不可解析”的直接来源日期格式化yyyy/MM/dd把2018-10-05变成2018/10/05使dt值内部携带/Hive 风格分区false关闭dt2018/10/05/hh10形式的目录名物理路径为2018/10/05/10写操作insert纯插入、无更新/删除配合 COW 表不需要日志文件物理目录形态写入后表在file:///tmp/hudi_non_extractable_partition_path下的目录结构为hudi_non_extractable_partition_path/ ├── .hoodie/ # Hudi 元数据表配置、commit 时间线 ├── 2018/10/05/10/ # dt2018-10-05, hh10 ├── 2018/10/06/5/ # dt2018-10-06, hh5 └── 2018/10/07/5/ # dt2018-10-07, hh5可以看到dt的取值被展开成了year/month/day三级目录再加上hh目录共 4 层。测试数据以压缩包形式随源码分发即同目录下的 hudi_non_extractable_partition_path.zip供ResourceHudiTablesInitializer在测试启动时解压到临时目录。三、从 Hive 元数据视角分区名与路径如何解耦“不可解析”的判定只发生在从路径反推分区值的环节。而查询引擎Trino / Hive读取分区时存在两条信息通道Hive Metastore 分区元数据分区名dt2018-10-05/hh10与分区值[2018-10-05, 10]由元数据表显式保存物理目录路径如2018/10/05/10需要分区提取器从路径段反推。只要查询引擎信任通道 1就不需要通道 2 的解析能力这也是该测试表能被正常查询的根本原因。在 Trino Hudi 连接器中这一逻辑体现在 HiveHudiPartitionInfo.java分区对象直接来自 Hive MetastorePartition其partition.getValues()保存了解耦后的分区列值例如[2018-10-05, 10]getRelativePartitionPath()通过截取表基础目录之后的相对路径得到2018/10/05/10仅用于定位物理文件buildPartitionKeys(partitionColumnHandles, partition.getValues())实现在 HudiUtil.java用元数据中的值构造HivePartitionKey而不是从路径解析doesMatchPredicates()调用 partitionMatchesPredicates把分区值与查询谓词dt2018-10-05 AND hh10做域匹配实现分区剪枝。也就是说Trino 的 Hudi 连接器沿用了 Hive 的“分区元数据驱动”模式分区值是元数据属性目录路径只是文件定位依据两者互不依赖。这恰好绕过了 Hudi 分区提取器在路径解析上的限制。四、测试初始化器把 zip 测试数据变成可查询的表hudi-trino的集成测试通过 ResourceHudiTablesInitializer.java 把资源目录下的 zip 表数据装载成 Trino 可查询的外部表流程分三步解压HudiTableUnzipper.unzipAllItemsInResource(hudi-testing-data, tempDir)把资源目录下所有 zip 解压到 JVM 临时目录并跳过.crc校验文件Hudi 遇到 crc 文件会报错拷贝copyDir将解压内容复制到连接器可访问的存储位置并对每个文件做 SHA-256 校验保证拷贝完整性注册元数据对TestingTable枚举中的每个表用HiveMetastore.createTable注册外部表再按partitions映射逐个注册 Hive 分区。对于本场景枚举项定义在 ResourceHudiTablesInitializer.javaHUDI_NON_EXTRACTABLE_PARTITION_PATH(multiPartitionRegularColumns(), multiPartitionColumns(), multiPartitionsWithNonExtractablePartitionPaths(), false),其中分区映射为multiPartitionsWithNonExtractablePartitionPathsHive 分区名元数据物理目录路径存储dt2018-10-05/hh102018/10/05/10dt2018-10-06/hh52018/10/06/5注意第二个分区dt2018-10-06/hh5对应的物理路径为2018/10/06/5只占 4 层中的 3 个目录层级10月06日 5点。两种形态都体现了“元数据分区名 ≠ 物理路径段数”这一核心特征。注册分区时代码调用extractPartitionValues(partitionName)来自HivePartitionManager从分区名中提取值[2018-10-05, 10]再通过tablePath.appendPath(partitionPath)定位物理目录——分区值与路径的绑定关系完全由这份显式映射决定与 Hudi 分区提取器无关。表结构与列定义初始化器为该表注册了 5 个 Hudi 元数据列加 3 个数据列multiPartitionRegularColumns元数据列_hoodie_commit_time、_hoodie_commit_seqno、_hoodie_record_key、_hoodie_partition_path、_hoodie_file_nameSTRING 类型见 HUDI_META_COLUMNS数据列idBIGINT、nameSTRING、tsBIGINT分区列dtSTRING、hhSTRING。这组定义与创建脚本中 Spark 写入的 schemaid/name/ts/dt/hh一一对应存储格式为 Hudi Parquet InputFormatHUDI_PARQUET_INPUT_FORMAT按 RO 表只读快照注册。五、Trino 端到端查询验证该场景对应的集成测试位于 TestHudiSmokeTest.javaTest public void testReadNonExtractablePartitionPathTable() { Session session SessionBuilder.from(getSession()) .withMdtEnabled(true) .build(); String res getQueryRunner().execute(session, SELECT * FROM HUDI_NON_EXTRACTABLE_PARTITION_PATH).toString(); System.out.println(res); assertQuery(session, SELECT name FROM HUDI_NON_EXTRACTABLE_PARTITION_PATH where dt2018-10-05, SELECT * FROM VALUES (Alice), (Bob)); assertQuery(session, SELECT name FROM HUDI_NON_EXTRACTABLE_PARTITION_PATH where dt2018-10-05 and hh10, SELECT * FROM VALUES (Alice), (Bob)); }测试验证了两个能力全表扫描SELECT *能完整读出 4 条记录说明文件定位不依赖路径解析分区谓词过滤dt2018-10-05与dt2018-10-05 AND hh10都能准确返回Alice、Bob两行说明分区剪枝依据的是元数据分区值而非物理路径段。这里withMdtEnabled(true)开启了 metadata table 支持对应会话属性hudi.metadata_enabled——即使不开启只读快照查询同样依赖 Hive 分区元数据完成。查询背后的 split 生成链路Trino 查询该表时分区处理由 HudiPartitionInfoLoader.java 驱动流程为HudiPartitionInfoLoader从分区队列取出HiveHudiPartitionInfo调用hudiDirectoryLister.listStatus(hudiPartitionInfo, useIndex)以分区对象的getRelativePartitionPath()如2018/10/05/10为入口列出该目录下的 FileSlice对每个 FileSlice由hudiSplitFactory.createSplits(partitionKeys, slice, commitTime)生成ConnectorSplit其中partitionKeys来自元数据值。整个链路中物理路径只作为目录遍历的输入分区值始终取自 Hive Metastore这正是“不可解析分区路径”表仍可被正常查询的实现基础。六、实践要点与适用边界何时会碰到该场景使用TimestampBasedKeyGenerator等自定义键生成器、或写入前手动把日期列格式化为yyyy/MM/dd等含斜杠形态时物理分区路径就会与分区列失去一一对应关系。日常yyyy-MM-dd等无斜杠格式不受影响。查询引擎依赖读取这类表必须依赖 Hive Metastore 的分区元数据或 Hudi Metadata Table。如果只凭文件系统目录结构做“路径即分区”的推断就会因段数不匹配而解析失败或漏数据。Hive 风格分区与不可解析路径的区分hoodie.datasource.write.hive_style_partitioningtrue会生成dt2018/10/05/hh10这种“键值”目录虽然也包含斜杠但键信息在目录名中显式存在因此可以被KeyGrouper类提取器解析而本场景关闭了该选项目录中不携带键名才是真正意义上的“不可解析”。COW 与无日志文件的组合本表选择 COW insert-only确保每个 FileSlice 只有 base file避免 MOR 场景下日志文件合并带来的额外复杂度把测试关注点收敛在“分区路径解析”这一个变量上。七、相关源码与测试索引关注点文件建表脚本与表结构说明hudi_non_extractable_partition_path.md测试数据压缩包hudi_non_extractable_partition_path.zip不可解析分区映射定义ResourceHudiTablesInitializer.java分区注册与外部表创建ResourceHudiTablesInitializer.java分区对象建模与相对路径提取HiveHudiPartitionInfo.java分区谓词匹配与分区键构建HudiUtil.javasplit 生成与分区遍历HudiPartitionInfoLoader.java端到端查询验证测试TestHudiSmokeTest.java若需要复现可在hudi-trino模块下运行TestHudiSmokeTest#testReadNonExtractablePartitionPathTable该测试会解压hudi-testing-data资源、注册外部表并执行上述查询断言。理解“分区名/分区值存在于元数据、目录路径仅用于定位文件”这一设计是正确处理 Hudi 各类非常规分区形态的关键。赞分享数据湖湖仓一体大数据数据存储【免费下载链接】hudiUpserts, Deletes And Incremental Processing on Big Data.项目地址https://gitcode.com/gh_mirrors/hud/hudi点击查看免费下载相关推荐Hudi 多分区字段 MOR 表的 Spark SQL 建表与 Trino 分区裁剪实战基于 hudi-trino 测试数据集Hudi 多分区字段 MOR 表的 Spark SQL 建表与 Trino 分区裁剪实战基于 hudi trino 测试数据集 导读 本文以 Apache数据湖湖仓一体大数据数据存储留痕WeChatMsg快速教程把微信消息存成搜得到的永久档案留痕WeChatMsg快速教程把微信消息存成搜得到的永久档案 你以为在对话框里点下删除那些消息就人间蒸发了未必。只要电脑版微信装过、登录过绝大多数历数据湖湖仓一体大数据数据存储DataHub Redshift 元数据采集实战指南从权限配置、Lineage 到 Usage 与 ProfilingDataHub Redshift 元数据采集实战指南从权限配置、Lineage 到 Usage 与 Profiling 导读 本文以 DataHub 官方 R数据湖湖仓一体大数据数据存储上一篇3分钟解锁全球游戏Locale Remulator区域模拟器终极指南下一篇MongoDB分片集群IDURAR ERP CRM的大规模数据存储终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考