Spark HiveMetastoreCatalog Infer Schema
问题一
说明
报错显示读取 Parquet footer 错误,不过所读取的文件不是查询条件指定的分区。
1 | Driver stacktrace: |
排查
关键信息:
1 | org.apache.spark.sql.execution.datasources.parquet.ParquetFileFormat.inferSchema(ParquetFileFormat.scala:241) |
通过对错误栈进行代码分析,锁定 ParquetFileFormat.inferSchema 和 HiveMetastoreCatalog.inferIfNeeded 两个方法。发现错误是发生在 Parquet 文件的 Schema 推断中。
问题二
说明
Driver 连接不上,像是卡住了,然后就直接退出,查询Application日志没有错误日志,查看Executor 的日志显示 java.io.IOException: Connection reset by peer
1 | Application application_1574672087080_11986014 failed 1 times due to ApplicationMaster for attempt appattempt_1574672087080_11986014_000001 timed out. Failing the application. |
排查
这个错误没有什么关键的错误信息,一般看到 Connection reset by peer(连接被重置)错误和 timed out 错误,想到调整超时时间,设置参数: spark.network.timeout=1200s,不过发现并没有用,还没有达到此时间就报错了。
查看 ApplicationMaster 所在的机器,对 ApplicationMaster(Driver) 的线程栈进行分析,jstack 打印线程栈信息,发现关键信息如下:
1 | "Driver" #38 prio=5 os_prio=0 tid=0x00002b504f82a000 nid=0x78b0 runnable [0x00002b50809a9000] |
根据问题一中的经验,查询 InMemoryFileIndex 日志,发现 InMemoryFileIndex 扫描两万多目录。
1 | InMemoryFileIndex: Listing leaf files and directories in parallel under: hdfs://..... |
问题解决
查看 org.apache.spark.sql.hive.HiveMetastoreCatalog#inferIfNeeded 代码如下:
1 | private def inferIfNeeded( |
shouldInfer 变量为 true 时会进行 Schema 推断,那么如何设置让它不进行推断呢?有下面两种情况:
- 设置 spark.sql.hive.caseSensitiveInferenceMode=NEVER_INFER
- schemaPreservesCase 为 true 时也跳过 infer schema,需要 Hive 表有 spark.sql.sources.schema.* 相关配置,并且 schema 和 table.schema 相等(相关判断在org.apache.spark.sql.hive.HiveExternalCatalog#restoreHiveSerdeTable里面)。
后面选择设置 spark.sql.hive.caseSensitiveInferenceMode=NEVER_INFER,关闭 Schema 推断解决问题。在 Spark3.0 中已经将此配置默认设置为 NEVER_INFER。
相关说明
为何有 Schema 推断
由于 Hive Schema 是不区分大小写,Parquet 文件的 Schema 是区分大小写的,读取有大小名称的 Parquet 文件时可能会导致结果有问题。caseSensitiveInferenceMode 的三种模式说明
caseSensitiveInferenceMode 有三种模式
INFER_AND_SAVE:此模式会在第一次进行Schema推断,然后保持到Hive表的properties里面(spark.sql.sources.schema.*)。
INFER_ONLY:进行推断,不会保持
NEVER_INFER:不进行推断