本文针对spark的spark.sql.hive.caseSensitiveInferenceMode的参数含义及使用进行梳理、总结
1. 参数含义
Spark 2.1.1引入了一个新的配置项: spark.sql.hive.caseSensitiveInferenceMode,默认值是NEVER_INFER,保持与spark 2.1.0一致的行为。但是Spark 2.2.0将此配置的默认值更改为INFER_AND_SAVE,以恢复读取底层文件schema中包含大小写混合列名的Hive元存储表的兼容性。使用INFER_AND_SAVE时,在第一次访问时,Spark将对任何没有保存推断的schema结果的Hive metastore table执行schema推断。注意,对于具有数千个分区的表来说,schema推断是一项非常耗时的操作。 如果不考虑与大小写混合列名的兼容性,您可以安全地将spark.sql.hive.caseSensitiveInferenceMode设置为NEVER_INFER,以避免schema推断的初始开销。注意,使用新的默认设置INFER_AND_SAVE,schema推断的结果将作为元存储键保存,以备将来使用。因此,初始schema推断只发生在表的第一次访问时。
从Spark 2.2.1和2.3.0,当数据源表具有partition schema和 data schema中都存在的列时,总是在运行时推断schema。推断的schema没有分区列。Spark在读取表时,只考虑这些重叠列的分区值,而不考虑数据源文件中存储的值。在2.2.0和2.1.x发布后,推断的schema被分区,但表的数据对用户是不可见的(即结果集是空的)。
在Spark 2.4及更早的版本中,当读取带有Spark原生数据源(parquet/orc)的Hive Serde表时,Spark会推断出实际的文件schema,并在metastore中更新表schema。从Spark 3.0开始,Spark不再推断schema。这应该不会给最终用户造成任何问题,但如果有,请将spark.sql.hive.caseSensitiveInferenceMode设置为INFER_AND_SAVE。
从spark不同版本来看,spark.sql.hive.caseSensitiveInferenceMode的默认行为先从不推荐schema, 然后变为运行时推断Schema,但最后又变为不推断。
注意:需要根据自己使用的spark 版本,调整此参数的值,以避免产生不可预料的结果,如读取hive表超级慢、内存溢出
2. 源码
spark.sql.hive.caseSensitiveInferenceMode有3种模式,推断并保存(spark 2.4.x默认模式)、仅推断、从不推断
推断并保存是指推断底层数据文件的大小写敏感的schema,并写回hive表的属性。
从不推断是指回退到使用不区分大小写的元存储Schema而不是推断
当不能从Hive表的属性中读取区分大小写的schema时,spark根据此参数采用不同的行为。
虽然Spark SQL本身不是区分大小写的,但是Hive兼容的文件格式,比如Parquet,是区分大小写的
Spark查询后端存储文件包含大小写敏感的列名称时,必须使用保留大小写的schema, 否则返回的结果可能不准确。
object HiveCaseSensitiveInferenceMode extends Enumeration {
val INFER_AND_SAVE, INFER_ONLY, NEVER_INFER = Value
}
val HIVE_CASE_SENSITIVE_INFERENCE = buildConf("spark.sql.hive.caseSensitiveInferenceMode")
.doc("Sets the action to take when a case-sensitive schema cannot be read from a Hive " +
"table's properties. Although Spark SQL itself is not case-sensitive, Hive compatible file " +
"formats such as Parquet are. Spark SQL must use a case-preserving schema when querying " +
"any table backed by files containing case-sensitive field names or queries may not return " +
"accurate results. Valid options include INFER_AND_SAVE (the default mode-- infer the " +
"case-sensitive schema from the underlying data files and write it back to the table " +
"properties), INFER_ONLY (infer the schema but don't attempt to write it to the table " +
"properties) and NEVER_INFER (fallback to using the case-insensitive metastore schema " +
"instead of inferring).")
.stringConf
.transform(_.toUpperCase(Locale.ROOT))
.checkValues(HiveCaseSensitiveInferenceMode.values.map(_.toString))
.createWithDefault(HiveCaseSensitiveInferenceMode.INFER_AND_SAVE.toString)
参考
https://spark.apache.org/docs/2.4.0/sql-migration-guide-upgrade.html
https://spark.apache.org/docs/3.0.0-preview/sql-migration-guide.html
https://issues.apache.org/jira/browse/SPARK-20888
https://issues.apache.org/jira/browse/SPARK-22306