hive分区不可见问题 - modric2jeff/archive GitHub Wiki
这是一个典型的 Hive Metastore (HMS) 状态与底层存储不一致 的问题,尤其是在分区数量突破 10万 这个量级时,元数据压力和 Hudi 的路径解析逻辑极易出现偏差。
既然 Spark 直接读取 Parquet 文件正常,说明数据物理层面没问题,问题出在 元数据映射 或 查询优化器的过滤阶段。以下是针对该场景的排查路径和解决方案:
即使 Hive 显示分区存在,其记录的物理路径(Location)可能与实际 Hudi 写入的路径不符。
-
检查分区路径:
SQLDESCRIBE FORMATTED table_name PARTITION (partition_col='xxx');确认输出中的
Location路径下是否真的存在对应的 Parquet 文件。 -
检查分区分数限制:
Hive 和 Spark 对单次查询拉取的分区数量有默认限制。如果你的查询没有加严谨的分区过滤,或者过滤后依然涉及大量分区,可能会触发静默失败或空返回。
检查 Hive 配置:
hive.metastore.limit.partition.fields检查 Spark 配置:
spark.sql.sources.parallelPartitionDiscovery.threshold
由于你是使用 Hudi 构建的外部分区表,最常见的诱因是 Hudi Metadata Table 或 同步标记异常。
-
禁用 Hudi 元数据表尝试:
Hudi 默认可能开启了 Metadata Table 来加速文件列表获取。如果元数据表不同步,查询就会落空。在 Spark SQL 中尝试关闭它:
SQLset hoodie.metadata.enable=false; SELECT * FROM table_name WHERE partition_col='xxx'; -
检查
.hoodie目录下的 Commit:确认最近一次写入分区的
commit或delta_commit是否成功完成。如果存在.inflight或.requested状态的同名文件且没有.completed,Hive 增量拉取视图可能认为该数据不可见。
Spark 直接读 Parquet 用的是 ParquetFileFormat,而通过 Hive/Spark SQL 读的是 Hudi 定义的 HoodieParquetInputFormat。
-
修复 Hive 统计信息:
有时候元数据中的
numRows被记录为 0 或 -1,某些优化器可能会直接返回空结果。SQLANALYZE TABLE table_name PARTITION (partition_col='xxx') COMPUTE STATISTICS; -
强制刷新 Spark 缓存:
如果你在同一个长期运行的 ThriftServer 或 Notebook 中查询:
SQLREFRESH TABLE table_name;
手段 | 目的 | 操作 -- | -- | -- Hudi CLI 检查 | 验证 Hudi 视图 | 使用 show partitions 和 files 命令查看 Hudi 内部对该分区的认知。 查看 HMS Log | 捕捉超时 | 检查 Hive Metastore 日志中是否有 GetPartitionsByFilter 引起的 Timeout 或 Memory Overflow。 文件过滤分析 | 确认文件可见性 | 在 Spark SQL 查询前 set spark.sql.hive.convertMetastoreParquet=false;。这会强制 Spark 使用 Hive 的 SerDe 而不是内置的优化读取器,帮助定位是否是 Spark 优化器的锅。
当分区达到 10 万级别,Hive Metastore 的查询效率会急剧下降,甚至出现逻辑错误:
分区同步工具: 确保你使用的是
org.apache.hudi.hive.MultiPartKeysValueExtractor(如果是多级分区)。增量同步: 检查 Hudi 写入任务中的
hoodie.datasource.hive_sync.mode。如果使用jdbc模式同步,大数据量下极不稳定,建议切换到hms模式。治理建议: 对于 10 万+分区的表,建议开启 Hudi Metadata Table 并定期进行
COMPACTION,同时考虑是否可以进行分区合并(例如从按小时分区改为按天分区)。
下一步建议:
您可以先尝试执行 set hoodie.metadata.enable=false; 后再进行查询。如果此时能查出数据,说明问题锁定在 Hudi 元数据表不同步;如果依然为空,建议提供一下 DESCRIBE FORMATTED 分区的结果,我帮您看看路径映射是否有误。
ALTER TABLE db.table DROP IF EXISTS PARTITION (PRODUCTNO='1001', RECORDDATE='2022-01-01'); ALTER TABLE db.table DROP IF EXISTS PARTITION (PRODUCTNO='1001', RECORDDATE='2022-01-02');