迁移指南:SQL、数据集和数据框
- 从 Spark SQL 3.5.1 升级到 3.5.2
- 从 Spark SQL 3.5.0 升级到 3.5.1
- 从 Spark SQL 3.4 升级到 3.5
- 从 Spark SQL 3.3 升级到 3.4
- 从 Spark SQL 3.2 升级到 3.3
- 从 Spark SQL 3.1 升级到 3.2
- 从 Spark SQL 3.0 升级到 3.1
- 从 Spark SQL 3.0.1 升级到 3.0.2
- 从 Spark SQL 3.0 升级到 3.0.1
- 从 Spark SQL 2.4 升级到 3.0
- 从 Spark SQL 2.4.7 升级到 2.4.8
- 从 Spark SQL 2.4.5 升级到 2.4.6
- 从 Spark SQL 2.4.4 升级到 2.4.5
- 从 Spark SQL 2.4.3 升级到 2.4.4
- 从 Spark SQL 2.4 升级到 2.4.1
- 从 Spark SQL 2.3 升级到 2.4
- 从 Spark SQL 2.2 升级到 2.3
- 从 Spark SQL 2.1 升级到 2.2
- 从 Spark SQL 2.0 升级到 2.1
- 从 Spark SQL 1.6 升级到 2.0
- 从 Spark SQL 1.5 升级到 1.6
- 从 Spark SQL 1.4 升级到 1.5
- 从 Spark SQL 1.3 升级到 1.4
- 从 Spark SQL 1.0-1.2 升级到 1.3
- 与 Apache Hive 的兼容性
从 Spark SQL 3.5.1 升级到 3.5.2
- 从3.5.2开始,MySQL JDBC数据源将TINYINT UNSIGNED读取为ShortType,而在3.5.1中,它错误地被读取为ByteType。
从 Spark SQL 3.5.0 升级到 3.5.1
- 自 Spark 3.5.1 起,MySQL JDBC 数据源将 TINYINT(n > 1) 和 TINYINT UNSIGNED 读取为 ByteType,而在 Spark 3.5.0 及以下版本中,它们被读取为 IntegerType。要恢复
从 Spark SQL 3.4 升级到 3.5
-
自 Spark 3.5 起,与 DS V2 下推相关的 JDBC 选项默认为
true。这些选项包括:pushDownAggregate、pushDownLimit、pushDownOffset和pushDownTableSample。要恢复旧的行为,请将它们设置为false。例如,设置spark.sql.catalog.your_catalog_name.pushDownAggregate为false。 -
自 Spark 3.5 起,Spark thrift 服务器在取消正在运行的语句时会中断任务。要恢复之前的行为,将
spark.sql.thriftServer.interruptOnCancel设置为false。 -
自 Spark 3.5 起,Row 的 json 和 prettyJson 方法被移动到
ToJsonUtil。 -
自 Spark 3.5 起,
plan字段从AnalysisException移动到EnhancedAnalysisException。 -
自 Spark 3.5 起,
spark.sql.optimizer.canChangeCachedPlanOutputPartitioning默认启用。要恢复之前的行为,将spark.sql.optimizer.canChangeCachedPlanOutputPartitioning设置为false。 -
自 Spark 3.5 起,
array_insert函数对负索引为 1 基。它在输入数组的末尾插入新元素,对于索引 -1。要恢复之前的行为,将spark.sql.legacy.negativeIndexInArrayInsert设置为true。 -
自 Spark 3.5 起,当将间隔类型读取为日期或时间戳类型,或者读取较低精度的小数类型时,Avro 将抛出
AnalysisException。要恢复旧的行为,将spark.sql.legacy.avro.allowIncompatibleSchema设置为true
从 Spark SQL 3.3 升级到 3.4
-
自Spark 3.4起,带有明确列列表的INSERT INTO命令,如果列数少于目标表中的列数,将自动为剩余列添加相应的默认值(对于没有显式分配默认值的列,则为NULL)。在Spark 3.3或更早版本中,这些命令会失败,并返回错误,报告提供的列数与目标表中的列数不匹配。请注意,禁用
spark.sql.defaultColumn.useNullsForMissingDefaultValues将恢复之前的行为。 - 自Spark 3.4起,来自Teradata的Number或Number(*)将被视为Decimal(38,18)。在Spark 3.3或更早版本中,来自Teradata的Number或Number(*)将被视为Decimal(38, 0),因此小数部分将被去除。
-
自Spark 3.4起,v1数据库、表、永久视图和函数标识符将包括“spark_catalog”作为目录名称,如果定义了数据库,例如,表标识符将是:
spark_catalog.default.t。要恢复遗留行为,请将spark.sql.legacy.v1IdentifierNoCatalog设置为true。 -
自Spark 3.4起,当ANSI SQL模式(配置
spark.sql.ansi.enabled)开启时,Spark SQL在使用不存在的键获取映射值时总是返回NULL结果。在Spark 3.3或更早版本中,将会出现错误。 -
自Spark 3.4起,SQL CLI
spark-sql不会在AnalysisException的错误消息之前打印前缀Error in query:。 -
自Spark 3.4起,
split函数在regex参数为空时会忽略尾部空字符串。 -
自Spark 3.4起,
to_binary函数在输入无效的str时会抛出错误。使用try_to_binary来容忍无效输入,并返回NULL。-
有效的Base64字符串应包含base64字母表中的符号(A-Za-z0-9+/)、可选的填充(
=)和可选的空白字符。在转换时跳过空白字符,除非它们前面有填充符号。如果存在填充,它应该结束字符串并遵循RFC 4648 § 4中描述的规则。 - 有效的十六进制字符串应仅包含允许的符号(0-9A-Fa-f)。
-
有效的
fmt值是不区分大小写的hex、base64、utf-8、utf8。
-
有效的Base64字符串应包含base64字母表中的符号(A-Za-z0-9+/)、可选的填充(
-
自Spark 3.4起,Spark只会在创建分区时抛出
PartitionsAlreadyExistException,如果其中一些分区已经存在。在Spark 3.3或更早版本中,Spark可能会抛出PartitionsAlreadyExistException或PartitionAlreadyExistsException。 -
自Spark 3.4起,Spark将在ALTER PARTITION中对分区规范进行验证,以遵循
spark.sql.storeAssignmentPolicy的行为,如果类型转换失败,可能会导致异常,例如ALTER TABLE .. ADD PARTITION(p='a'),如果列p是int类型。要恢复遗留行为,请将spark.sql.legacy.skipTypeValidationOnAlterPartition设置为true。 -
自Spark 3.4起,对嵌套数据类型(数组、映射和结构)默认启用矢量化读取器。要恢复遗留行为,请将
spark.sql.orc.enableNestedColumnVectorizedReader和spark.sql.parquet.enableNestedColumnVectorizedReader设置为false。 -
自Spark 3.4起,CSV数据源不支持
BinaryType。在Spark 3.3或更早版本中,用户可以在CSV数据源中写入二进制列,但CSV文件中的输出内容是Object.toString(),这是没有意义的;同时,如果用户读取带有二进制列的CSV表,Spark将抛出Unsupported type: binary异常。 -
自Spark 3.4起,布隆过滤器连接默认启用。要恢复遗留行为,请将
spark.sql.optimizer.runtime.bloomFilter.enabled设置为false。 -
自Spark 3.4起,在外部Parquet文件上进行模式推断时,带有注释
isAdjustedToUTC=false的INT64时间戳将被推断为TimestampNTZ类型,而不是Timestamp类型。要恢复遗留行为,请将spark.sql.parquet.inferTimestampNTZ.enabled设置为false。 -
自Spark 3.4起,
CREATE TABLE AS SELECT ...的行为从OVERWRITE更改为APPEND,当spark.sql.legacy.allowNonEmptyLocationInCTAS设置为true时。建议用户避免在非空表位置上进行CTAS。
从 Spark SQL 3.2 升级到 3.3
-
自 Spark 3.3 起,Spark SQL 中的
histogram_numeric函数返回一个结构体数组(x,y)的输出类型,其中返回值中‘x’字段的类型由聚合函数中消耗的输入值传播。在 Spark 3.2 或更早版本中,‘x’ 的类型总是为双精度类型。可选地,使用配置spark.sql.legacy.histogramNumericPropagateInputType从 Spark 3.3 起恢复到先前的行为。 -
自 Spark 3.3 起,Spark SQL 中的
DayTimeIntervalType被映射到 Arrow 中的Duration类型,适用于ArrowWriter和ArrowColumnVector开发者 API。以前,DayTimeIntervalType被映射到 Arrow 的Interval类型,该类型与其他语言 Spark SQL 的映射类型不匹配。例如,DayTimeIntervalType在 Java 中映射为java.time.Duration。 -
自 Spark 3.3 起,
lpad和rpad函数进行了重载,以支持字节序列。当第一个参数为字节序列时,可选的填充模式也必须是字节序列,结果为 BINARY 值。在这种情况下,默认填充模式为零字节。要恢复始终返回字符串类型的旧行为,请将spark.sql.legacy.lpadRpadAlwaysReturnString设置为true。 -
自 Spark 3.3 起,当用户指定的模式包含非空字段时,Spark 将非可空模式转换为可空格式,适用于 API
DataFrameReader.schema(schema: StructType).json(jsonDataset: Dataset[String])和DataFrameReader.schema(schema: StructType).csv(csvDataset: Dataset[String])。要恢复尊重空值的旧行为,请将spark.sql.legacy.respectNullabilityInTextDatasetConversion设置为true。 -
自 Spark 3.3 起,当未指定日期或时间戳模式时,Spark 使用
CAST表达式方法将输入字符串转换为日期/时间戳。这些更改影响 CSV/JSON 数据源和分区值的解析。在 Spark 3.2 或更早版本中,当未设置日期或时间戳模式时,Spark 使用默认模式:yyyy-MM-dd用于日期,yyyy-MM-dd HH:mm:ss用于时间戳。更改后,Spark 仍然识别模式以及日期模式:
-
[+-]yyyy* -
[+-]yyyy*-[m]m -
[+-]yyyy*-[m]m-[d]d -
[+-]yyyy*-[m]m-[d]d -
[+-]yyyy*-[m]m-[d]d * -
[+-]yyyy*-[m]m-[d]dT*
时间戳模式:
-
[+-]yyyy* -
[+-]yyyy*-[m]m -
[+-]yyyy*-[m]m-[d]d -
[+-]yyyy*-[m]m-[d]d -
[+-]yyyy*-[m]m-[d]d [h]h:[m]m:[s]s.[ms][ms][ms][us][us][us][zone_id] -
[+-]yyyy*-[m]m-[d]dT[h]h:[m]m:[s]s.[ms][ms][ms][us][us][us][zone_id] -
[h]h:[m]m:[s]s.[ms][ms][ms][us][us][us][zone_id] -
T[h]h:[m]m:[s]s.[ms][ms][ms][us][us][us][zone_id]
-
-
自 Spark 3.3 起,
strfmt在format_string(strfmt, obj, ...)和printf(strfmt, obj, ...)中将不再支持使用 “0$” 来指定第一个参数,第一参数应该始终通过 “1$” 来引用,当使用参数索引指示参数在参数列表中的位置时。 -
自 Spark 3.3 起,默认情况下在 CSV 数据源中将 null 值写入为空字符串。在 Spark 3.2 或更早版本中,null 值被写入为空字符串的引用空字符串,
""。要恢复以前的行为,请将nullValue设置为"",或将配置spark.sql.legacy.nullValueWrittenAsQuotedEmptyStringCsv设置为true。 -
自 Spark 3.3 起,如果函数不存在,DESCRIBE FUNCTION 将失败。在 Spark 3.2 或更早版本中,DESCRIBE FUNCTION 仍然可以运行并打印 “Function: func_name not found”。
-
自 Spark 3.3 起,表属性
external变为保留字。如果您指定external属性,则某些命令会失败,例如CREATE TABLE ... TBLPROPERTIES和ALTER TABLE ... SET TBLPROPERTIES。在 Spark 3.2 和更早版本中,表属性external被静默忽略。您可以将spark.sql.legacy.notReserveProperties设置为true以恢复旧行为。 -
自 Spark 3.3 起,DROP FUNCTION 会失败,如果函数名称与内置函数的名称匹配且未经过限定。在 Spark 3.2 或更早版本中,即使名称未经过限定且与内置函数的名称相同,DROP FUNCTION 仍然可以删除持久性函数。
-
自 Spark 3.3 起,当读取 JSON 属性定义为
FloatType或DoubleType时,字符串"+Infinity"、"+INF"和"-INF"现在被解析为适当的值,此外,已经支持的"Infinity"和"-Infinity"变体。此更改旨在提高与 Jackson 解析这些值未加引号版本的一致性。此外,allowNonNumericNumbers选项现在被尊重,因此如果此选项被禁用,这些字符串将被视为无效。 -
自 Spark 3.3 起,Spark 将尝试在
INSERT OVERWRITE DIRECTORY中使用内置数据源写入器,而不是 Hive serde。此行为仅在spark.sql.hive.convertMetastoreParquet或spark.sql.hive.convertMetastoreOrc分别启用于 Parquet 和 ORC 格式时有效。要恢复 Spark 3.3 之前的行为,您可以将spark.sql.hive.convertMetastoreInsertDir设置为false。 -
自 Spark 3.3 起,类似 round 的函数的返回类型精度已被修复。这可能导致 Spark 抛出
AnalysisException的CANNOT_UP_CAST_DATATYPE错误类,当使用先前版本创建的视图时。在这种情况下,您需要使用 ALTER VIEW AS 或 CREATE OR REPLACE VIEW AS 通过较新的 Spark 版本重新创建视图。 -
自 Spark 3.3 起,
unbase64函数会对格式错误的str输入抛出错误。使用try_to_binary(处理格式错误的输入,返回 NULL。 在 Spark 3.2 和更早版本中,, 'base64') unbase64函数会对格式错误的str输入返回最优结果。 -
自 Spark 3.3 起,当读取不是由 Spark 生成的 Parquet 文件时,带有注释
isAdjustedToUTC = false的 Parquet 时间戳列在模式推断期间被推断为 TIMESTAMP_NTZ 类型。在 Spark 3.2 和更早版本中,这些列被推断为 TIMESTAMP 类型。要恢复 Spark 3.3 之前的行为,您可以将spark.sql.parquet.inferTimestampNTZ.enabled设置为false。 -
自 Spark 3.3.1和3.2.3 起,对于
SELECT ... GROUP BY a GROUPING SETS (b)风格的 SQL 语句,grouping__id返回的值与 Apache Spark 3.2.0、3.2.1、3.2.2 和 3.3.0 的值不同。它基于用户给定的 group-by 表达式加上分组集列进行计算。要恢复 3.3.1 和 3.2.3 之前的行为,您可以设置spark.sql.legacy.groupingIdWithAppendedUserGroupBy。有关详细信息,请参见 SPARK-40218 和 SPARK-40562 。
从 Spark SQL 3.1 升级到 3.2
-
自 Spark 3.2 起,ADD FILE/JAR/ARCHIVE 命令要求所有路径需被
"或'包围,如果路径包含空格。 -
自 Spark 3.2 起,所有支持的 JDBC 方言对 ROWID 使用 StringType。在 Spark 3.1 或更早版本中,Oracle 方言使用 StringType 而其他方言使用 LongType。
-
在 Spark 3.2 中,PostgreSQL JDBC 方言对 MONEY 使用 StringType,由于 PostgreSQL 的 JDBC 驱动程序无法正确处理这些类型,因此不支持 MONEY[]。在 Spark 3.1 或更早版本中,分别使用 DoubleType 和 DoubleType 的 ArrayType。
-
在 Spark 3.2 中,
spark.sql.adaptive.enabled默认启用。要恢复 Spark 3.2 之前的行为,可以将spark.sql.adaptive.enabled设置为false. -
在 Spark 3.2 中,以下元字符在
show()操作中被转义。在 Spark 3.1 或更早版本中,以下元字符将照原样输出。-
\n(换行) -
\r(回车) -
\t(水平制表符) -
\f(换页) -
\b(退格) -
\u000B(垂直制表符) -
\u0007(响铃)
-
-
在 Spark 3.2 中,
ALTER TABLE .. RENAME TO PARTITION在目标分区已经存在的情况下,会抛出PartitionAlreadyExistsException,而不是AnalysisException,适用于来自 Hive 外部的表。 -
在 Spark 3.2 中,脚本转换的默认字段分隔符为
\u0001,在无 serde 模式下;在用户指定 serde 的 Hive serde 模式下,serde 属性field.delim为\t。在 Spark 3.1 或更早版本中,默认字段分隔符为\t,用户在 Hive serde 模式下指定 serde 时,serde 属性field.delim为\u0001。 -
在 Spark 3.2 中,自动生成的
Cast(例如由类型强制转换规则添加的)在生成列别名时将被去掉。例如,sql("SELECT floor(1)").columns将是FLOOR(1),而不是FLOOR(CAST(1 AS DOUBLE))。 -
在 Spark 3.2 中,
SHOW TABLES的输出模式变为namespace: string, tableName: string, isTemporary: boolean。在 Spark 3.1 或更早版本中,namespace字段在内置目录中名为database,且 v2 目录中没有isTemporary字段。要恢复与内置目录的旧模式,可以将spark.sql.legacy.keepCommandOutputSchema设置为true。 -
在 Spark 3.2 中,
SHOW TABLE EXTENDED的输出模式变为namespace: string, tableName: string, isTemporary: boolean, information: string。在 Spark 3.1 或更早版本中,namespace字段在内置目录中名为database,而对 v2 目录没有更改。要恢复与内置目录的旧模式,可以将spark.sql.legacy.keepCommandOutputSchema设置为true。 -
在 Spark 3.2 中,
SHOW TBLPROPERTIES的输出模式变为key: string, value: string,无论您是否指定表属性键。在 Spark 3.1 及更早版本中,SHOW TBLPROPERTIES的输出模式为value: string,当您指定表属性键时。要恢复与内置目录的旧模式,可以将spark.sql.legacy.keepCommandOutputSchema设置为true。 -
在 Spark 3.2 中,
DESCRIBE NAMESPACE的输出模式变为info_name: string, info_value: string。在 Spark 3.1 或更早版本中,info_name字段在内置目录中名为database_description_item,而info_value字段在内置目录中名为database_description_value。要恢复与内置目录的旧模式,可以将spark.sql.legacy.keepCommandOutputSchema设置为true。 -
在 Spark 3.2 中,表刷新会清除表及其所有依赖项(如视图)的缓存数据,同时保留依赖项的缓存。以下命令执行表刷新:
-
ALTER TABLE .. ADD PARTITION -
ALTER TABLE .. RENAME PARTITION -
ALTER TABLE .. DROP PARTITION -
ALTER TABLE .. RECOVER PARTITIONS -
MSCK REPAIR TABLE -
LOAD DATA -
REFRESH TABLE -
TRUNCATE TABLE -
以及方法
spark.catalog.refreshTable在 Spark 3.1 和更早版本中,表刷新会使依赖项保持未缓存状态。
-
-
在 Spark 3.2 中,使用
count(tblName.*)会被阻止以避免产生歧义结果。因为如果存在任何空值,count(*)和count(tblName.*)将输出不同。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.allowStarWithSingleTableIdentifierInCount设置为true。 -
在 Spark 3.2 中,我们支持在 INSERT 和 ADD/DROP/RENAME PARTITION 的分区规范中使用类型字面量。例如,
ADD PARTITION(dt = date'2020-01-01')添加一个日期值为2020-01-01的分区。在 Spark 3.1 和更早版本中,分区值将被解析为字符串值date '2020-01-01',这是一个非法日期值,最终添加一个值为 null 的分区。 -
在 Spark 3.2 中,
DataFrameNaFunctions.replace()不再对输入列名使用精确字符串匹配,以匹配 SQL 语法并支持合格的列名。输入列名中包含点(非嵌套)需要用反引号 ` 转义。现在,如果在数据框架模式中找不到该列,将抛出AnalysisException。如果输入列名是嵌套列,则会抛出IllegalArgumentException。在 Spark 3.1 和更早版本中,它会忽略无效的输入列名和嵌套列名。 -
在 Spark 3.2 中,日期减法表达式如
date1 - date2返回DayTimeIntervalType类型。在 Spark 3.1 和更早版本中,返回类型是CalendarIntervalType。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.interval.enabled设置为true。 -
在 Spark 3.2 中,时间戳减法表达式如
timestamp '2021-03-31 23:48:00' - timestamp '2021-01-01 00:00:00'返回DayTimeIntervalType类型。在 Spark 3.1 和更早版本中,相同表达式的类型是CalendarIntervalType。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.interval.enabled设置为true。 -
在 Spark 3.2 中,
CREATE TABLE .. LIKE ..命令不能使用保留属性。您需要它们的特定子句来指定这些属性,例如,CREATE TABLE test1 LIKE test LOCATION 'some path'。您可以将spark.sql.legacy.notReserveProperties设置为true来忽略ParseException,在这种情况下,这些属性将被静默移除,例如:TBLPROPERTIES('owner'='yao')将没有任何效果。在 Spark 3.1 及更早版本中,保留属性可以在CREATE TABLE .. LIKE ..命令中使用,但没有副作用,例如,TBLPROPERTIES('location'='/tmp')并不会改变表的位置,而只是创建一个无头属性,就像'a'='b'一样。 -
在 Spark 3.2 中,
TRANSFORM操作符不支持输入别名。在 Spark 3.1 和更早版本中,我们可以编写脚本转换,如SELECT TRANSFORM(a AS c1, b AS c2) USING 'cat' FROM TBL。 -
在 Spark 3.2 中,
TRANSFORM操作符可以支持ArrayType/MapType/StructType,而无需 Hive SerDe,在这种模式下,我们使用StructsToJson将ArrayType/MapType/StructType列转换为STRING,并使用JsonToStructs将STRING解析为ArrayType/MapType/StructType。在 Spark 3.1 中,Spark 仅支持将ArrayType/MapType/StructType列视为STRING,但不支持将STRING解析为ArrayType/MapType/StructType输出列。 -
在 Spark 3.2 中,单位到单位的间隔字面量,如
INTERVAL '1-1' YEAR TO MONTH和单位列表间隔字面量,如INTERVAL '3' DAYS '1' HOUR被转换为 ANSI 间隔类型:YearMonthIntervalType或DayTimeIntervalType。在 Spark 3.1 和更早版本中,这些间隔字面量被转换为CalendarIntervalType。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.interval.enabled设置为true。 -
在 Spark 3.2 中,单位列表间隔字面量不能混合年月字段(YEAR 和 MONTH)和日期时间字段(WEEK, DAY, …, MICROSECOND)。例如,
INTERVAL 1 month 1 hour在 Spark 3.2 中是无效的。在 Spark 3.1 和更早版本中,没有此限制,字面量返回值为CalendarIntervalType。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.interval.enabled设置为true。 -
在 Spark 3.2 中,Spark 支持
DayTimeIntervalType和YearMonthIntervalType作为 HiveSERDE模式中TRANSFORM子句的输入和输出,这两种类型作为输入时,HiveSERDE模式与ROW FORMAT DELIMITED模式的行为不同。在 HiveSERDE模式下,DayTimeIntervalType列被转换为HiveIntervalDayTime,其字符串格式为[-]?d h:m:s.n,而在ROW FORMAT DELIMITED模式中,格式为INTERVAL '[-]?d h:m:s.n' DAY TO TIME。在 HiveSERDE模式中,YearMonthIntervalType列被转换为HiveIntervalYearMonth,其字符串格式为[-]?y-m,而在ROW FORMAT DELIMITED模式中,格式为INTERVAL '[-]?y-m' YEAR TO MONTH。 -
在 Spark 3.2 中,
hash(0) == hash(-0)对于浮点类型。之前生成不同的值。 -
在 Spark 3.2 中,带有非空
LOCATION的CREATE TABLE AS SELECT将抛出AnalysisException。要恢复 Spark 3.2 之前的行为,可以将spark.sql.legacy.allowNonEmptyLocationInCTAS设置为true。 -
在 Spark 3.2 中,特殊日期时间值,如
epoch、today、yesterday、tomorrow和now仅支持在类型字面量或在可折叠字符串的强制转换中,例如,select timestamp'now'或select cast('today' as date)。在 Spark 3.1 和 3.0 中,这些特殊值在任何字符串转换为日期/时间戳中都得到了支持。要在 Spark 3.1 和 3.0 中保留这些特殊值作为日期/时间戳,您应该手动替换它们,例如,if (c in ('now', 'today'), current_date(), cast(c as date)). -
在 Spark 3.2 中,
FloatType被映射为 MySQL 中的FLOAT。在此之前,它被映射为REAL,后者默认是 MySQL 中DOUBLE PRECISION的同义词。 -
在 Spark 3.2 中,由
DataFrameWriter触发的查询执行在发送给QueryExecutionListener时总是命名为command。在 Spark 3.1 和更早版本中,名称为save、insertInto、saveAsTable之一。 -
在 Spark 3.2 中,
Dataset.unionByName将allowMissingColumns设置为 true 时,会将缺失的嵌套字段添加到结构体的末尾。在 Spark 3.1 中,嵌套结构字段是按字母顺序排序的。 -
在 Spark 3.2 中,如果输入查询输出列包含自动生成的别名,则创建/修改视图将失败。这是必要的,以确保查询输出列名在不同的 Spark 版本之间是稳定的。要恢复 Spark 3.2 之前的行为,将
spark.sql.legacy.allowAutoGeneratedAliasForView设置为true。 -
在 Spark 3.2 中,日期 +/- 间隔,且仅有日期时间字段,例如
date '2011-11-11' + interval 12 hours返回时间戳。在 Spark 3.1 和更早版本中,相同表达式返回日期。要恢复 Spark 3.2 之前的行为,您可以使用cast将时间戳转换为日期。
从 Spark SQL 3.0 升级到 3.1
-
在 Spark 3.1 中,统计聚合函数包括
std、stddev、stddev_samp、variance、var_samp、skewness、kurtosis、covar_samp和corr在表达式评估期间发生DivideByZero时将返回NULL而不是Double.NaN,例如,当stddev_samp应用于单个元素集合时。在 Spark 3.0 和更早版本中,在这种情况下将返回Double.NaN。要恢复 Spark 3.1 之前的行为,可以将spark.sql.legacy.statisticalAggregate设置为true。 -
在 Spark 3.1 中,
grouping_id()返回长整型值。在 Spark 3.0 和更早版本中,此函数返回整型值。要恢复 Spark 3.1 之前的行为,可以将spark.sql.legacy.integerGroupingId设置为true。 -
在 Spark 3.1 中,SQL UI 数据采用
formatted模式显示查询计划解释结果。要恢复 Spark 3.1 之前的行为,可以将spark.sql.ui.explainMode设置为extended。 -
在 Spark 3.1 中,
from_unixtime、unix_timestamp、to_unix_timestamp、to_timestamp和to_date如果指定的日期时间模式无效,将会失败。在 Spark 3.0 或更早版本中,它们会返回NULL。 -
在 Spark 3.1 中,Parquet、ORC、Avro 和 JSON 数据源在读取时如果检测到顶级列和嵌套结构中的重复名称,会抛出异常
org.apache.spark.sql.AnalysisException: Found duplicate column(s) in the data schema。数据源在检测列名重复时会考虑 SQL 配置spark.sql.caseSensitive。 -
在 Spark 3.1 中,结构体和映射在转换为字符串时被用
{}括起来。例如,show()操作和CAST表达式使用这样的括号。在 Spark 3.0 和更早版本中,使用[]括号达到同样的目的。要恢复 Spark 3.1 之前的行为,可以将spark.sql.legacy.castComplexTypesToString.enabled设置为true。 -
在 Spark 3.1 中,结构体、数组和映射的 NULL 元素在转换为字符串时被转换为 “null”。在 Spark 3.0 或更早版本中,NULL 元素被转换为空字符串。要恢复 Spark 3.1 之前的行为,可以将
spark.sql.legacy.castComplexTypesToString.enabled设置为true。 -
在 Spark 3.1 中,当
spark.sql.ansi.enabled为 false 时,Spark 如果十进制类型列的总和溢出,始终返回 NULL。在 Spark 3.0 或更早版本中,在这种情况下,十进制类型列的总和可能返回 NULL 或不正确的结果,甚至在运行时失败(具体取决于实际查询计划执行)。 -
在 Spark 3.1 中,当调用带有路径参数的方法时,
path选项不能共存:DataFrameReader.load()、DataFrameWriter.save()、DataStreamReader.load(),或DataStreamWriter.start()。此外,paths选项不能共存于DataFrameReader.load()中。例如,spark.read.format("csv").option("path", "/tmp").load("/tmp2")或spark.read.option("path", "/tmp").csv("/tmp2")会抛出org.apache.spark.sql.AnalysisException。在 Spark 3.0 及更早版本中,如果传递一个路径参数给上述方法,path选项会被覆盖;如果传递多个路径参数给DataFrameReader.load(),path选项会被添加到总体路径中。要恢复 Spark 3.1 之前的行为,可以将spark.sql.legacy.pathOptionBehavior.enabled设置为true。 -
在 Spark 3.1 中,对于不完整的间隔字面量,例如
INTERVAL '1'、INTERVAL '1 DAY 2',将返回IllegalArgumentException,因为它们是无效的。在 Spark 3.0 中,这些字面量返回NULL。 -
在 Spark 3.1 中,我们移除了内置的 Hive 1.2。您需要将自定义 SerDes 迁移到 Hive 2.3。有关更多详细信息,请参阅 HIVE-15167 。
-
在 Spark 3.1 中,加载和保存来自/到 parquet 文件的时间戳如果时间戳早于 1900-01-01 00:00:00Z,并作为 INT96 类型加载(保存),将会失败。在 Spark 3.0 中,这些操作不会失败,但可能由于从/到朱利安历和法定公历的重基导致输入时间戳的偏移。要恢复 Spark 3.1 之前的行为,可以将
spark.sql.legacy.parquet.int96RebaseModeInRead和/或spark.sql.legacy.parquet.int96RebaseModeInWrite设置为LEGACY。 -
在 Spark 3.1 中,
schema_of_json和schema_of_csv函数返回字段名称带引号的 SQL 格式的模式。在 Spark 3.0 中,该函数返回没有字段引号且为小写的目录字符串。 -
在 Spark 3.1 中,刷新表将触发对所有引用该表的其他缓存的取消缓存操作,即使该表本身没有被缓存。在 Spark 3.0 中,这一操作仅在该表本身被缓存的情况下触发。
-
在 Spark 3.1 中,创建或更改永久视图将捕获运行时 SQL 配置,并将其存储为视图属性。这些配置将在视图解析的解析和分析阶段应用。要恢复 Spark 3.1 之前的行为,可以将
spark.sql.legacy.useCurrentConfigsForView设置为true。 -
在 Spark 3.1 中,临时视图将与永久视图具有相同的行为,即捕获并存储运行时 SQL 配置、SQL 文本、目录和命名空间。捕获的视图属性将在视图解析的解析和分析阶段应用。要恢复 Spark 3.1 之前的行为,可以将
spark.sql.legacy.storeAnalyzedPlanForView设置为true。 -
在 Spark 3.1 中,通过
CACHE TABLE ... AS SELECT创建的临时视图也将具有与永久视图相同的行为。特别是,当临时视图被删除时,Spark 将使其所有缓存依赖失效,以及临时视图本身的缓存。这与 Spark 3.0 及更早版本不同,后者只会导致临时视图本身缓存的失效。要恢复先前的行为,可以将spark.sql.legacy.storeAnalyzedPlanForView设置为true。 -
自 Spark 3.1 起,CHAR/CHARACTER 和 VARCHAR 类型在表架构中得到了支持。表扫描/插入将尊重 char/varchar 语义。如果在表架构以外的地方使用 char/varchar,将抛出异常(CAST 是一个例外,仅将 char/varchar 视为字符串,如之前一样)。要恢复 Spark 3.1 之前的行为,将它们视为 STRING 类型并忽略长度参数,例如
CHAR(4),可以将spark.sql.legacy.charVarcharAsString设置为true。 -
在 Spark 3.1 中,对于来自 Hive 外部目录的表,在以下情况下会用其子类替换
AnalysisException:-
ALTER TABLE .. ADD PARTITION如果新分区已经存在,将抛出PartitionsAlreadyExistException -
ALTER TABLE .. DROP PARTITION对于不存在的分区将抛出NoSuchPartitionsException
-
从 Spark SQL 3.0.1 升级到 3.0.2
-
在 Spark 3.0.2 中,
AnalysisException被其子类替代,这些子类在以下情况下对来自 Hive 外部目录的表进行抛出:-
ALTER TABLE .. ADD PARTITION如果新分区已经存在,则抛出PartitionsAlreadyExistException -
ALTER TABLE .. DROP PARTITION对于不存在的分区抛出NoSuchPartitionsException
-
-
在 Spark 3.0.2 中,
PARTITION(col=null)在分区规范中始终被解析为一个 null 字面量。在 Spark 3.0.1 或更早版本中,如果分区列是字符串类型,则它被解析为其文本表示的字符串字面量,例如,字符串“null”。要恢复旧的行为,可以将spark.sql.legacy.parseNullPartitionSpecAsStringLiteral设置为 true。 -
在 Spark 3.0.2 中,
SHOW DATABASES的输出模式变为namespace: string。在 Spark 3.0.1 及更早版本中,模式为databaseName: string。自 Spark 3.0.2 以来,您可以通过将spark.sql.legacy.keepCommandOutputSchema设置为true来恢复旧模式。
从 Spark SQL 3.0 升级到 3.0.1
-
在 Spark 3.0 中,JSON 数据源和 JSON 函数
schema_of_json如果字符串值与 JSON 选项timestampFormat定义的模式匹配,则推断为 TimestampType。从版本 3.0.1 开始,时间戳类型推断默认禁用。将 JSON 选项inferTimestamp设置为true以启用此类类型推断。 -
在 Spark 3.0 中,当将字符串转换为整数类型(tinyint, smallint, int 和 bigint)、日期时间类型(date, timestamp 和 interval)和布尔类型时,前导和尾随字符(<= ASCII 32)将被删除。例如,
cast('\b1\b' as int)的结果是1。从 Spark 3.0.1 开始,仅前导和尾随的空白 ASCII 字符将被删除。例如,cast('\t1\t' as int)的结果是1但cast('\b1\b' as int)的结果是NULL。
从 Spark SQL 2.4 升级到 3.0
数据集/数据框 API
-
在 Spark 3.0 中,Dataset 和 DataFrame API
unionAll不再被弃用。它是union的别名。 -
在 Spark 2.4 及更早版本中,如果键是非结构类型,例如 int、string、array 等,
Dataset.groupByKey结果的分组数据集的键属性错误地被命名为“value”。这不符合直觉,使聚合查询的模式出乎意料。例如,ds.groupByKey(...).count()的模式是(value, count)。自 Spark 3.0 起,我们将分组属性命名为“key”。在新添加的配置spark.sql.legacy.dataset.nameNonStructGroupingKeyAsValue下保留旧行为,默认值为false。 -
在 Spark 3.0 中,列的元数据将始终在 API
Column.name和Column.as中传播。在 Spark 2.4 及更早版本中,NamedExpression的元数据在调用 API 时被设置为新列的explicitMetadata,即使底层的NamedExpression更改了元数据,也不会发生变化。要恢复 Spark 3.0 之前的行为,您可以使用带有显式元数据的 APIas(alias: String, metadata: Metadata)。 -
在将一个 Dataset 转换为另一个 Dataset 时,Spark 将原始 Dataset 中的字段向上转换为目标 DataSet 中对应字段的类型。在 2.4 及更早版本中,这种向上转换不太严格,例如
Seq("str").toDS.as[Int]失败,但Seq("str").toDS.as[Boolean]有效并在执行期间抛出 NPE。在 Spark 3.0 中,向上转换变得更严格,不允许将 String 转换为其他类型,即Seq("str").toDS.as[Boolean]会在分析期间失败。要恢复 Spark 3.0 之前的行为,设置spark.sql.legacy.doLooseUpcast为true。
DDL 语句
-
在 Spark 3.0 中,当将一个值插入到不同数据类型的表列时,类型强制转换按照 ANSI SQL 标准进行。某些不合理的类型转换,例如将
string转换为int和double转换为boolean是不允许的。如果值超出了列的数据类型范围,将抛出运行时异常。在 Spark 2.4 及以下版本中,只要它们是有效的Cast,在表插入时允许类型转换。当将超出范围的值插入整数字段时,插入的是值的低位部分(与 Java/Scala 数值类型转换相同)。例如,如果将 257 插入到字节类型字段,结果是 1。该行为由选项spark.sql.storeAssignmentPolicy控制,默认值为“ANSI”。将该选项设置为“Legacy”将恢复之前的行为。 -
之前
ADD JAR命令返回的结果集包含单个值 0。现在返回一个空的结果集。 -
Spark 2.4 及以下版本:即使指定的键是
SparkConf条目,并且它没有影响,因为该命令不会更新SparkConf,但该行为可能会让用户困惑,SET命令在此情况下不会发出任何警告。在 3.0 中,如果使用SparkConf键,命令会失败。你可以通过将spark.sql.legacy.setCommandRejectsSparkCoreConfs设置为false来禁用此检查。 -
刷新缓存表会触发表的取消缓存操作,然后是表的缓存(惰性)操作。在 Spark 2.4 及以下版本中,取消缓存操作之前缓存名称和存储级别不会被保留。因此,缓存名称和存储级别可能会意外改变。在 Spark 3.0 中,首先保留缓存名称和存储级别以便于缓存的重建。这有助于在表刷新时保持一致的缓存行为。
-
在 Spark 3.0 中,下面列出的属性变为保留属性;如果你在
CREATE DATABASE ... WITH DBPROPERTIES和ALTER TABLE ... SET TBLPROPERTIES等地方指定保留属性,则命令会失败。你需要它们的特定子句来指定它们,例如CREATE DATABASE test COMMENT '任何评论' LOCATION '某个路径'。你可以将spark.sql.legacy.notReserveProperties设置为true来忽略ParseException,在这种情况下,这些属性将被静默移除,例如:SET DBPROPERTIES('location'='/tmp')将没有效果。在 Spark 2.4 及以下版本中,这些属性既不是保留的也没有副作用,例如,SET DBPROPERTIES('location'='/tmp')不会改变数据库的位置,而只是创建一个无头属性,就像'a'='b'一样。属性(区分大小写) 数据库保留 表保留 备注 provider 否 是 对于表,使用 USING子句来指定。一旦设置,就不能更改。location 是 是 对于数据库和表,使用 LOCATION子句来指定。owner 是 是 对于数据库和表,由运行 Spark 并创建表的用户决定。 -
在 Spark 3.0 中,你可以使用
ADD FILE来添加文件目录。之前,你只能使用此命令添加单个文件。要恢复早期版本的行为,将spark.sql.legacy.addSingleFileInAddFile设置为true。 -
在 Spark 3.0 中,如果表不存在,
SHOW TBLPROPERTIES会抛出AnalysisException。在 Spark 2.4 及以下版本中,此情况会导致NoSuchTableException。 -
在 Spark 3.0 中,
SHOW CREATE TABLE table_identifier始终返回 Spark DDL,即使给定的表是 Hive SerDe 表。要生成 Hive DDL,请使用SHOW CREATE TABLE table_identifier AS SERDE命令。 -
在 Spark 3.0 中,CHAR 类型的列不允许出现在非 Hive SerDe 表中,如果检测到 CHAR 类型,CREATE/ALTER TABLE 命令将会失败。请改用 STRING 类型。在 Spark 2.4 及以下版本中,CHAR 类型被视为 STRING 类型,长度参数将被简单忽略。
用户定义函数和内置函数
-
在 Spark 3.0 中,
date_add和date_sub函数仅接受 int、smallint、tinyint 作为第二个参数;小数和非文字字符串不再有效,例如:date_add(cast('1964-05-23' as date), '12.34')会导致AnalysisException。请注意,字符串文字仍然被允许,但是如果字符串内容不是有效的整数,Spark 会抛出AnalysisException。在 Spark 2.4 及更早版本中,如果第二个参数是小数或字符串值,则会被强制转换为整数值,结果是日期值1964-06-04。 -
在 Spark 3.0 中,
percentile_approx函数及其别名approx_percentile仅接受范围在[1, 2147483647]的整数值作为第三个参数accuracy,小数和字符串类型不被允许,例如,percentile_approx(10.0, 0.2, 1.8D)会导致AnalysisException。在 Spark 2.4 及更早版本中,如果accuracy是小数或字符串值,则被强制转换为 int 值,percentile_approx(10.0, 0.2, 1.8D)被处理为percentile_approx(10.0, 0.2, 1),结果为10.0。 -
在 Spark 3.0 中,当对
MapType的元素应用哈希表达式时,会抛出分析异常。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.allowHashOnMapType设置为true。 -
在 Spark 3.0 中,当
array/map函数在没有任何参数的情况下被调用时,会返回一个元素类型为NullType的空集合。在 Spark 2.4 及更早版本中,会返回一个元素类型为StringType的空集合。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.createEmptyCollectionUsingStringType设置为true。 -
在 Spark 3.0 中,
from_json函数支持两种模式 -PERMISSIVE和FAILFAST。可以通过mode选项设置模式。默认模式变为PERMISSIVE。在之前的版本中,from_json的行为不符合PERMISSIVE或FAILFAST,尤其是在处理格式错误的 JSON 记录时。例如,JSON 字符串{"a" 1},其模式a INT在之前的版本中转换为null,而 Spark 3.0 将其转换为Row(null)。 -
在 Spark 2.4 及更早版本中,您可以通过内置函数如
CreateMap、MapFromArrays等使用 map 类型键创建 map 值。在 Spark 3.0 中,不允许使用这些内置函数创建带有 map 类型键的 map 值。用户可以使用map_entries函数将 map 转换为 array -
在 Spark 2.4 及更早版本中,您可以通过内置函数如
CreateMap、StringToMap等创建带有重复键的 map。重复键的 map 行为未定义,例如,map 查找遵循重复键出现的顺序,Dataset.collect仅保留最后出现的重复键,MapKeys返回重复键等。在 Spark 3.0 中,当发现重复键时,Spark 会抛出RuntimeException。你可以将spark.sql.mapKeyDedupPolicy设置为LAST_WIN以使用最后的胜利策略去重 map 键。用户仍然可以从不强制执行的(例如 Parquet)数据源读取重复键的 map 值,但行为是未定义的。 -
在 Spark 3.0 中,默认不允许使用
org.apache.spark.sql.functions.udf(AnyRef, DataType)。建议去掉返回类型参数以自动切换到类型化的 Scala udf,或者将spark.sql.legacy.allowUntypedScalaUDF设置为 true 以继续使用它。在 Spark 2.4 及更早版本中,如果org.apache.spark.sql.functions.udf(AnyRef, DataType)得到一个带有基本类型参数的 Scala 闭包,当输入值为 null 时返回的 UDF 返回 null。然而,在 Spark 3.0 中,如果输入值为 null,UDF 返回Java 类型的默认值。例如,val f = udf((x: Int) => x, IntegerType),如果列x为 null,则f($"x")在 Spark 2.4 及更早版本中返回 null,而在 Spark 3.0 中返回 0。此行为的变化是由于 Spark 3.0 默认使用 Scala 2.12 构建的。 -
在 Spark 3.0 中,高阶函数
exists遵循三值布尔逻辑,也就是说,如果predicate返回任何null并且没有获得true,则exists返回null而不是false。例如,exists(array(1, null, 3), x -> x % 2 == 0)为null。可以通过将spark.sql.legacy.followThreeValuedLogicInArrayExists设置为false来恢复先前的行为。 -
在 Spark 3.0 中,
add_months函数不会在原始日期为月份的最后一天时调整结果日期到该月份的最后一天。例如,select add_months(DATE'2019-02-28', 1)的结果为2019-03-28。在 Spark 2.4 及更早版本中,当原始日期为月份的最后一天时,结果日期会进行调整。例如,将一个月添加到2019-02-28的结果为2019-03-31。 -
在 Spark 2.4 及更早版本中,
current_timestamp函数仅返回毫秒级分辨率的时间戳。在 Spark 3.0 中,如果系统上可用的底层时钟提供微秒级分辨率,则该函数可以返回微秒级分辨率的结果。 -
在 Spark 3.0 中,带有 0 个参数的 Java UDF 在执行器端与其他 UDF 相同地执行。在 Spark 2.4 及更早版本中,仅带有 0 个参数的 Java UDF 在驱动程序端执行,结果被传播到执行器,这在某些情况下可能更高效,但在某些情况下导致一致性问题。
-
java.lang.Math的log、log1p、exp、expm1和pow的结果可能因平台而异。在 Spark 3.0 中,相应 SQL 函数的结果(包括相关的 SQL 函数如LOG10)返回的值与java.lang.StrictMath一致。在几乎所有情况下,这不会影响返回值,并且差异很小,但在像log(3.0)这种情况下,java.lang.Math和java.lang.StrictMath的值可能不会完全相同。 -
在 Spark 3.0 中,
cast函数在将字符串文字如 ‘Infinity’、‘+Infinity’、‘-Infinity’、‘NaN’、‘Inf’、‘+Inf’、‘-Inf’ 强制转换为Double或Float类型时,以不区分大小写的方式处理这些字符串文字,以确保与其他数据库系统更高的兼容性。该行为变化在下表中说明:操作 Spark 3.0 之前的结果 Spark 3.0 中的结果 CAST(‘infinity’ AS DOUBLE) NULL Double.PositiveInfinity CAST(‘+infinity’ AS DOUBLE) NULL Double.PositiveInfinity CAST(‘inf’ AS DOUBLE) NULL Double.PositiveInfinity CAST(‘inf’ AS DOUBLE) NULL Double.PositiveInfinity CAST(‘-infinity’ AS DOUBLE) NULL Double.NegativeInfinity CAST(‘-inf’ AS DOUBLE) NULL Double.NegativeInfinity CAST(‘infinity’ AS FLOAT) NULL Float.PositiveInfinity CAST(‘+infinity’ AS FLOAT) NULL Float.PositiveInfinity CAST(‘inf’ AS FLOAT) NULL Float.PositiveInfinity CAST(‘+inf’ AS FLOAT) NULL Float.PositiveInfinity CAST(‘-infinity’ AS FLOAT) NULL Float.NegativeInfinity CAST(‘-inf’ AS FLOAT) NULL Float.NegativeInfinity CAST(‘nan’ AS DOUBLE) NULL Double.NaN CAST(‘nan’ AS FLOAT) NULL Float.NaN -
在 Spark 3.0 中,将区间值强制转换到字符串类型时,没有“interval”前缀,例如,
1 days 2 hours。在 Spark 2.4 及更早版本中,字符串包含“interval”前缀,如interval 1 days 2 hours。 -
在 Spark 3.0 中,将字符串值强制转换为整型(tinyint、smallint、int 和 bigint)、日期时间类型(date、timestamp 和 interval)和布尔类型时,前后空格(<= ASCII 32)将在转换为这些类型值之前被修剪,例如,
cast(' 1\t' as int)的结果为1,cast(' 1\t' as boolean)的结果为true,cast('2019-10-10\t as date)的结果为日期值2019-10-10。在 Spark 2.4 及更早版本中,将字符串转换为整型和布尔型时,不会去掉两端的空格;上述结果是null,而到日期时间类型时,仅去掉后面的空格(= ASCII 32)。
查询引擎
-
在 Spark 2.4 及以下版本中,诸如
FROM或FROM的 SQL 查询是意外支持的。在 hive 风格的UNION ALL FROM
FROM中,SELECT
SELECT子句不可忽视。Hive 和 Presto 都不支持这种语法。这些查询在 Spark 3.0 中被视为无效。 -
在 Spark 3.0 中,区间字面量语法不再允许多个 from-to 单位。例如,
SELECT INTERVAL '1-1' YEAR TO MONTH '2-2' YEAR TO MONTH'会抛出解析器异常。 -
在 Spark 3.0 中,以科学记数法写的数字(例如,
1E2)将被解析为 Double。在 Spark 2.4 及以下版本中,它们被解析为 Decimal。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.exponentLiteralAsDecimal.enabled设置为true。 -
在 Spark 3.0 中,日期时间区间字符串根据
from和to边界转换为区间。如果输入字符串与指定边界定义的模式不匹配,将抛出ParseException异常。例如,interval '2 10:20' hour to minute引发异常,因为预期格式为[+|-]h[h]:[m]m。在 Spark 2.4 版本中,from边界没有被考虑,而to边界被用来截断结果区间。例如,显示示例中的日期时间区间字符串被转换为interval 10 hours 20 minutes。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.fromDayTimeString.enabled设置为true。 -
在 Spark 3.0 中,负小数的尺度默认不被允许,例如,像
1E10BD的字面值的数据类型为DecimalType(11, 0)。在 Spark 2.4 及以下版本中,它是DecimalType(2, -9)。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.allowNegativeScaleOfDecimal设置为true。 -
在 Spark 3.0 中,单目算术运算符加 (
+) 只接受字符串、数字和区间类型的值作为输入。此外,带有整数字符串表示的+被强制转换为双精度值,例如,+'1'返回1.0。在 Spark 2.4 及以下版本中,这个运算符被忽略。没有类型检查,因此,所有带有+前缀的类型值都是有效的,例如,+ array(1, 2)是有效的,结果为[1, 2]。此外,根本没有类型强制转换,例如,在 Spark 2.4 中,+'1'的结果是字符串1。 -
在 Spark 3.0 中,如果 Dataset 查询包含由于自连接造成的模糊列引用,则查询失败。一个典型的例子是:
val df1 = ...; val df2 = df1.filter(...);,然后df1.join(df2, df1("a") > df2("a"))返回一个空结果,这相当令人困惑。这是因为 Spark 无法解析指向正在自连接的表的 Dataset 列引用,而df1("a")在 Spark 中与df2("a")完全相同。要恢复 Spark 3.0 之前的行为,可以将spark.sql.analyzer.failAmbiguousSelfJoin设置为false。 -
在 Spark 3.0 中,引入了
spark.sql.legacy.ctePrecedencePolicy来控制嵌套 WITH 子句中命名冲突的行为。默认值为EXCEPTION,Spark 会抛出 AnalysisException,强迫用户选择他们想要的特定替代顺序。如果设置为CORRECTED(推荐),内部 CTE 定义优先于外部定义。例如,将配置设置为false,WITH t AS (SELECT 1), t2 AS (WITH t AS (SELECT 2) SELECT * FROM t) SELECT * FROM t2返回2,而将其设置为LEGACY,结果为1,这是版本 2.4 及以下的行为。 -
在 Spark 3.0 中,配置
spark.sql.crossJoin.enabled变为内部配置,默认值为 true,因此默认情况下 spark 不会对包含隐式交叉连接的 SQL 抛出异常。 -
在 Spark 2.4 及以下版本中,float/double -0.0 在语义上等于 0.0,但在聚合分组键、窗口分区键和连接键中,-0.0 和 0.0 被视为不同的值。在 Spark 3.0 中,这个 bug 被修复。例如,
Seq(-0.0, 0.0).toDF("d").groupBy("d").count()在 Spark 3.0 中返回[(0.0, 2)],而在 Spark 2.4 及以下版本中返回[(0.0, 1), (-0.0, 1)]。 -
在 Spark 2.4 及以下版本中,无效时区 ID 被静默忽略,并被 GMT 时区替换,例如在 from_utc_timestamp 函数中。在 Spark 3.0 中,这种时区 ID 被拒绝,Spark 将抛出
java.time.DateTimeException。 -
在 Spark 3.0 中,解析、格式化和转换日期和时间戳时使用公历,同时在提取年份、天数等子组件时也使用。Spark 3.0 使用基于 ISO 年表 的
java.time包中的 Java 8 API 类。在 Spark 2.4 及以下版本中,这些操作是使用混合日历 ( 儒略 + 公历 ) 执行的。这些更改影响 1582 年 10 月 15 日(公历)之前的日期的结果,并影响以下 Spark 3.0 API:-
时间戳/日期字符串的解析/格式化。这影响 CSV/JSON 数据源以及
unix_timestamp、date_format、to_unix_timestamp、from_unixtime、to_date、to_timestamp函数,当用户指定的模式用于解析和格式化时。在 Spark 3.0 中,我们在 用于格式化和解析的日期时间模式 中定义自己的模式字符串,通过 DateTimeFormatter 在后台实现。新实现对其输入进行严格检查。例如,时间戳2015-07-22 10:00:00如果模式为yyyy-MM-dd不能解析,因为解析器没有消耗整个输入。另一个例子是31/01/2015 00:00输入不能通过dd/MM/yyyy hh:mm模式解析,因为hh假设小时在1-12的范围内。在 Spark 2.4 及以下版本中,java.text.SimpleDateFormat被用于时间戳/日期字符串的转换,支持的模式在 SimpleDateFormat 中描述。可以通过将spark.sql.legacy.timeParserPolicy设置为LEGACY来恢复旧行为。 -
weekofyear、weekday、dayofweek、date_trunc、from_utc_timestamp、to_utc_timestamp和unix_timestamp函数使用 java.time API 计算年份的周数、星期的天数,以及从/到 UTC 时区内的 TimestampType 值的转换。 -
JDBC 选项
lowerBound和upperBound被转换为 TimestampType/DateType 值,类似于将字符串转换为 TimestampType/DateType 值的方式。转换基于公历,并使用 SQL 配置spark.sql.session.timeZone定义的时区。在 Spark 2.4 及以下版本中,转换基于混合日历(儒略 + 公历)和默认系统时区。 -
格式化
TIMESTAMP和DATE字面值。 -
从字符串创建类型化的
TIMESTAMP和DATE字面值。在 Spark 3.0 中,对类型化TIMESTAMP/DATE字面值的字符串转换是通过强制转换为TIMESTAMP/DATE值执行的。例如,TIMESTAMP '2019-12-23 12:59:30'在语义上等于CAST('2019-12-23 12:59:30' AS TIMESTAMP)。当输入字符串不包含时区信息时,将使用 SQL 配置spark.sql.session.timeZone中的时区。在 Spark 2.4 及以下版本中,转换基于 JVM 系统时区。默认时区的不同来源可能会改变类型化TIMESTAMP和DATE字面值的行为。
-
-
在 Spark 3.0 中,
TIMESTAMP字面值使用 SQL 配置spark.sql.session.timeZone转换为字符串。在 Spark 2.4 及以下版本中,转换使用 Java 虚拟机的默认时区。 -
在 Spark 3.0 中,Spark 在与日期/时间戳的二进制比较中将
String强制转换为Date/Timestamp。可以通过将spark.sql.legacy.typeCoercion.datetimeToString.enabled设置为true来恢复Date/Timestamp转换为String的先前行为。 -
在 Spark 3.0 中,支持从字符串转换为日期和时间戳的特殊值。这些值只是记号性简写,在读取时转换为普通的日期或时间戳值。以下字符串值支持用于日期:
-
epoch [zoneId]- 1970-01-01 -
today [zoneId]- 在spark.sql.session.timeZone指定的时区中的当前日期 -
yesterday [zoneId]- 当前日期 - 1 -
tomorrow [zoneId]- 当前日期 + 1 -
now- 当前查询的运行日期。它具有与今天相同的概念
例如,
SELECT date 'tomorrow' - date 'yesterday';应该输出2。以下是特殊的时间戳值:-
epoch [zoneId]- 1970-01-01 00:00:00+00(Unix 系统时间零点) -
today [zoneId]- 今天午夜 -
yesterday [zoneId]- 昨天午夜 -
tomorrow [zoneId]- 明天午夜 -
now- 当前查询开始时间
例如
SELECT timestamp 'tomorrow';。 -
-
自 Spark 3.0 起,在使用
EXTRACT表达式从日期/时间戳值中提取第二字段时,结果将是具有 2 位数的第二部分和 6 位小数部分的DecimalType(8, 6)值,具微秒精度。例如,extract(second from to_timestamp('2019-09-20 10:10:10.1'))结果为10.100000。在 Spark 2.4 和更早版本中,它返回IntegerType值,前一个例子的结果为10。 -
在 Spark 3.0 中,日期时间模式字母
F是 对齐的月份中的星期几 ,表示一个周期内计数的天数,其中星期是对齐到月份的开始。在 Spark 2.4 及更早版本中,它是 月份的周数 ,表示一个月内以固定的星期几开始的周数的计数。例如,2020-07-30是在该月的第一天之后 30 天(4 周和 2 天),因此date_format(date '2020-07-30', 'F')在 Spark 3.0 中返回 2,但在 Spark 2.x 中作为周数返回 5,因为它位于 2020 年 7 月的第 5 周,其中第一个星期是 2020-07-01 到 07-04。 -
在 Spark 3.0 中,Spark 将尝试在
CTAS中使用内置数据源写入器,而不是 Hive serde。此行为仅在分别为 Parquet 和 ORC 格式启用spark.sql.hive.convertMetastoreParquet或spark.sql.hive.convertMetastoreOrc时有效。要恢复 Spark 3.0 之前的行为,可以将spark.sql.hive.convertMetastoreCtas设置为false。 -
在 Spark 3.0 中,Spark 将尝试使用内置数据源写入器,而不是 Hive serde,处理使用 HiveSQL 语法创建的分区 ORC/Parquet 表的插入。此行为仅在分别为 Parquet 和 ORC 格式启用
spark.sql.hive.convertMetastoreParquet或spark.sql.hive.convertMetastoreOrc时有效。要恢复 Spark 3.0 之前的行为,可以将spark.sql.hive.convertInsertingPartitionedTable设置为false。
数据源
-
在 Spark 2.4 及以下版本中,当使用 Spark 原生数据源(parquet/orc)读取 Hive SerDe 表时,Spark 会推断实际文件模式并更新元数据存储中的表模式。在 Spark 3.0 中,Spark 不再推断模式。这不应该对终端用户造成任何问题,但如果确实如此,请将
spark.sql.hive.caseSensitiveInferenceMode设置为INFER_AND_SAVE。 -
在 Spark 2.4 及以下版本中,如果分区列值无法被转换为用户提供的对应模式,则该值将被转换为 null。在 3.0 中,分区列值会与用户提供的模式进行验证。如果验证失败,将抛出异常。您可以通过将
spark.sql.sources.validatePartitionColumns设置为false来禁用这种验证。 -
在 Spark 3.0 中,如果在递归目录列出过程中文件或子目录消失(即它们出现在中间列出中,但在递归目录列出的后续阶段无法读取或列出,原因可能是并发文件删除或对象存储一致性问题),则列出将失败并抛出异常,除非
spark.sql.files.ignoreMissingFiles设置为true(默认值为false)。在以前的版本中,这些缺失的文件或子目录将被忽略。请注意,这种行为的变化仅适用于初始表文件列出(或在REFRESH TABLE期间),而不在查询执行期间:净变化是spark.sql.files.ignoreMissingFiles现在在表文件列出/查询规划时被遵循,而不仅仅是在查询执行时。 -
在 Spark 2.4 及以下版本中,JSON 数据源的解析器将空字符串视为某些数据类型的 null,例如
IntegerType。对于FloatType、DoubleType、DateType和TimestampType,空字符串会导致失败并抛出异常。Spark 3.0 不允许空字符串,并将为除StringType和BinaryType之外的数据类型抛出异常。通过将spark.sql.legacy.json.allowEmptyString.enabled设置为true可以恢复允许空字符串的先前行为。 -
在 Spark 2.4 及以下版本中,JSON 数据源和 JSON 函数如
from_json在指定模式为StructType的情况下将有问题的 JSON 记录转换为所有null的行,而在宽松模式下。 在 Spark 3.0 中,如果某些 JSON 列值被成功解析并转换为期望的数据类型,则返回的行可以包含非null字段。 -
在 Spark 3.0 中,JSON 数据源和 JSON 函数
schema_of_json从字符串值推断 TimestampType,如果它们匹配由 JSON 选项timestampFormat定义的模式。将 JSON 选项inferTimestamp设置为false以禁用这种类型推断。 -
在 Spark 2.4 及以下版本中,CSV 数据源将格式不正确的 CSV 字符串转换为在宽松模式下的所有
null的行。在 Spark 3.0 中,如果某些 CSV 列值被成功解析并转换为目标类型,则返回的行可以包含非null字段。 -
在 Spark 3.0 中,当使用用户提供的模式写入 Avro 文件时,字段通过字段名称在 Catalyst 模式和 Avro 模式之间进行匹配,而不是通过位置。
-
在 Spark 3.0 中,当使用用户提供的非可空模式写入 Avro 文件时,即使 Catalyst 模式是可空的,Spark 仍然能够写入文件。但是,如果任何记录包含 null,则 Spark 将抛出运行时 NullPointerException。
-
在 Spark 2.4 及以下版本中,当输入文件的开头有 BOM 时,CSV 数据源可以自动检测输入文件的编码。例如,CSV 数据源可以识别 UTF-8、UTF-16BE、UTF-16LE、UTF-32BE 和 UTF-32LE(在多行模式下,即 CSV 选项
multiLine设置为true)。在 Spark 3.0 中,CSV 数据源按照通过 CSV 选项encoding指定的编码读取输入文件,默认值为 UTF-8。通过这种方式,如果文件编码与通过 CSV 选项指定的编码不匹配,则 Spark 将错误地加载该文件。为了解决这个问题,用户应通过 CSV 选项encoding设置正确的编码,或者将选项设置为null,这将在 Spark 3.0 之前的版本中回退到编码自动检测。
其他
-
在Spark 2.4版本中,当通过
cloneSession()创建Spark会话时,新创建的Spark会话会从其父SparkContext中继承其配置,即使在其父Spark会话中可能存在相同的配置但值不同。 在Spark 3.0中,父SparkSession的配置优先级高于父SparkContext。 您可以通过将spark.sql.legacy.sessionInitWithConfigDefaults设置为true来恢复旧行为。 -
在Spark 3.0中,如果
hive.default.fileformat在Spark SQL配置中未找到,则它会回退到SparkContext的Hadoop配置中存在的hive-site.xml文件。 -
在Spark 3.0中,我们将内置Hive从1.2升级到2.3,并带来了以下影响:
-
您可能需要根据要连接的Hive元存储版本设置
spark.sql.hive.metastore.version和spark.sql.hive.metastore.jars。例如:如果您的Hive元存储版本是1.2.1,则将spark.sql.hive.metastore.version设置为1.2.1,并将spark.sql.hive.metastore.jars设置为maven。 -
您需要将自定义SerDes迁移到Hive 2.3,或使用
hive-1.2配置构建自己的Spark。有关更多详细信息,请参见 HIVE-15167 。 -
在使用
TRANSFORM操作符进行脚本转换的SQL中,Hive 1.2和Hive 2.3之间的十进制字符串表示可能有所不同,这取决于Hive的行为。在Hive 1.2中,字符串表示省略尾部零。 但在Hive 2.3中,如果需要,始终填充到18位并带有尾部零。
-
从 Spark SQL 2.4.7 升级到 2.4.8
-
在 Spark 2.4.8 中,
AnalysisException被其子类替代,这些子类在以下情况下会针对来自 Hive 外部目录的表被抛出:-
ALTER TABLE .. ADD PARTITION会抛出PartitionsAlreadyExistException如果新分区已存在 -
ALTER TABLE .. DROP PARTITION会抛出NoSuchPartitionsException对于不存在的分区
-
从 Spark SQL 2.4.5 升级到 2.4.6
-
在 Spark 2.4.6 中,
RESET命令不会将静态 SQL 配置值重置为默认值。它只会清除运行时 SQL 配置值。
从 Spark SQL 2.4.4 升级到 2.4.5
-
从 Spark 2.4.5 开始,
TRUNCATE TABLE命令在重新创建表/分区路径时会尝试恢复原始权限和 ACL。要恢复早期版本的行为,请将spark.sql.truncateTable.ignorePermissionAcl.enabled设置为true。 -
从 Spark 2.4.5 开始,添加了
spark.sql.legacy.mssqlserver.numericMapping.enabled配置,以支持使用 IntegerType 和 DoubleType 对 SMALLINT 和 REAL JDBC 类型进行遗留的 MsSQLServer 方言映射行为。要恢复 2.4.3 及更早版本的行为,请将spark.sql.legacy.mssqlserver.numericMapping.enabled设置为true。
从 Spark SQL 2.4.3 升级到 2.4.4
- 自 Spark 2.4.4 起,根据 MsSqlServer 指南 ,MsSQLServer JDBC 方言分别使用 ShortType 和 FloatType 来表示 SMALLINT 和 REAL。之前使用的是 IntegerType 和 DoubleType。
从 Spark SQL 2.4 升级到 2.4.1
-
当指定不带单位的
spark.executor.heartbeatInterval值时,例如“30”而不是“30s”,在Spark 2.4.0的不同代码部分中,它的解释是不一致的,有时被解释为秒,有时被解释为毫秒。现在无单位值被一致地解释为毫秒。那些设置值为“30”的应用现在需要指定带单位的值,比如“30s”,以避免被解释为毫秒;否则,导致的极短间隔可能会使应用失败。
从 Spark SQL 2.3 升级到 2.4
-
在 Spark 2.3 及更早版本中,
array_contains函数的第二个参数被隐式提升为第一个数组类型参数的元素类型。这种类型提升可能会导致信息丢失,并可能导致array_contains函数返回错误结果。该问题在 2.4 中通过采用更安全的类型提升机制得到了修复。这可能会引起某些行为的变化,如下表所示。查询 Spark 2.3 或更早 Spark 2.4 备注 SELECT array_contains(array(1), 1.34D);truefalse在 Spark 2.4 中,左侧和右侧参数分别被提升为双精度类型数组和双精度类型。 SELECT array_contains(array(1), '1');trueAnalysisException被抛出。可以在参数中使用显式转换以避免异常。在 Spark 2.4 中,由于整数类型无法无损的提升到字符串类型,因此会抛出 AnalysisException。SELECT array_contains(array(1), 'anystring');nullAnalysisException被抛出。可以在参数中使用显式转换以避免异常。在 Spark 2.4 中,由于整数类型无法无损的提升到字符串类型,因此会抛出 AnalysisException。 -
自 Spark 2.4 起,在子查询之前的 IN 运算符前如果有一个结构字段,内部查询也必须包含一个结构字段。在之前的版本中,相反,结构的字段与内部查询的输出进行比较。例如,如果
a是一个struct(a string, b int),在 Spark 2.4 中a in (select (1 as a, 'a' as b) from range(1))是一个有效的查询,而a in (select 1, 'a' from range(1))不是。在之前的版本中情况正好相反。 -
在 2.2.1+ 和 2.3 版本中,如果
spark.sql.caseSensitive设置为 true,则CURRENT_DATE和CURRENT_TIMESTAMP函数错误地变得区分大小写,并且会解析为列名(除非以小写输入)。在 Spark 2.4 中,此问题已被修复,这些函数不再区分大小写。 -
自 Spark 2.4 起,Spark 将根据 SQL 标准遵循优先级规则来评估查询中的集合运算。如果未通过括号指定顺序,则集合运算从左到右执行,但是所有的 INTERSECT 运算在任何 UNION、EXCEPT 或 MINUS 运算之前执行。在新的配置
spark.sql.legacy.setopsPrecedence.enabled下,旧行为保持对所有集合运算给予相等优先级,默认值为false。当该属性设置为true时,Spark 将按照查询中出现的顺序从左到右评估集合运算符,而没有通过使用括号来强制执行明确的排序。 -
自 Spark 2.4 起,Spark 将在 Last Access 列中显示表描述值为 UNKNOWN,当该值为 1970 年 1 月 1 日时。
-
自 Spark 2.4 起,Spark 默认最大化对 ORC 文件的矢量化 ORC 阅读器的使用。为此,
spark.sql.orc.impl和spark.sql.orc.filterPushdown的默认值分别更改为native和true。由本地 ORC 写入器创建的 ORC 文件可能无法被某些旧的 Apache Hive 发行版读取。使用spark.sql.orc.impl=hive来创建与 Hive 2.1.1 及更早版本共享的文件。 -
自 Spark 2.4 起,将空的数据框写入目录会至少启动一个写任务,即使在物理上数据框没有分区。这引入了一个小的行为变化,对于自描述文件格式,如 Parquet 和 Orc,Spark 在写入 0 分区数据框时,会在目标目录中创建一个仅包含元数据的文件,以便用户在稍后读取该目录时,模式推断仍然可以工作。新行为在写入空数据框时更合理且更一致。
-
自 Spark 2.4 起,UDF 参数中的表达式 ID 不再出现在列名中。例如,在 Spark 2.4 中,列名不是
UDF:f(col0 AS colA#28)而是UDF:f(col0 AS `colA`)。 -
自 Spark 2.4 起,使用任何文件格式(parquet、orc、json、text、csv 等)写入具有空或嵌套空架构的数据框是不允许的。在尝试写入具有空架构的数据框时会抛出异常。
-
自 Spark 2.4 起,Spark 在将 DATE 类型与 TIMESTAMP 类型进行比较时,会先将两边都提升为 TIMESTAMP。将
false设置为spark.sql.legacy.compareDateTimestampInTimestamp会恢复之前的行为。此选项将在 Spark 3.0 中删除。 -
自 Spark 2.4 起,创建具有非空位置的托管表是不允许的。在尝试创建具有非空位置的托管表时会抛出异常。将
true设置为spark.sql.legacy.allowCreatingManagedTableUsingNonemptyLocation会恢复之前的行为。此选项将在 Spark 3.0 中删除。 -
自 Spark 2.4 起,将托管表重命名为现有位置是不允许的。在尝试将托管表重命名为现有位置时会抛出异常。
-
自 Spark 2.4 起,类型强制规则可以自动提升可变参数 SQL 函数(例如,IN/COALESCE)的参数类型为最宽的公共类型,而不论输入参数的顺序如何。在之前的 Spark 版本中,提升可能会在某些特定顺序(例如,TimestampType、IntegerType 和 StringType)中失败,并引发异常。
-
自 Spark 2.4 起,Spark 除了传统的缓存失效机制外,还启用了非级联 SQL 缓存失效。非级联缓存失效机制允许用户在不影响其依赖缓存的情况下删除缓存。这种新的缓存失效机制适用于缓存中要删除的数据仍然有效的情况,例如,对 Dataset 调用 unpersist() 或丢弃临时视图。这允许用户释放内存,同时保持所需缓存的有效性。
-
在 2.3 及更早版本中,Spark 默认转换 Parquet Hive 表,但忽略了表属性,例如
TBLPROPERTIES (parquet.compression 'NONE')。对于spark.sql.hive.convertMetastoreOrc=true的情况,这也会发生在 ORC Hive 表属性,如TBLPROPERTIES (orc.compress 'NONE')。自 Spark 2.4 起,Spark 在转换 Parquet/ORC Hive 表时尊重 Parquet/ORC 特定的表属性。举个例子,CREATE TABLE t(id int) STORED AS PARQUET TBLPROPERTIES (parquet.compression 'NONE')在 Spark 2.3 中会生成 Snappy parquet 文件,而在 Spark 2.4 中,结果将是未压缩的 parquet 文件。 -
自 Spark 2.0 起,Spark 默认转换 Parquet Hive 表以提高性能。自 Spark 2.4 起,Spark 还默认转换 ORC Hive 表。这意味着 Spark 默认使用自己的 ORC 支持,而不是 Hive SerDe。举个例子,
CREATE TABLE t(id int) STORED AS ORC在 Spark 2.3 中会用 Hive SerDe 处理,而在 Spark 2.4 中,则会转换为 Spark 的 ORC 数据源表,并应用 ORC 矢量化。将false设置为spark.sql.hive.convertMetastoreOrc会恢复之前的行为。 -
在 2.3 及更早版本中,如果 CSV 行中的至少一个列值格式错误,则该行被视为格式错误。CSV 解析器在 DROPMALFORMED 模式下丢弃这些行,或者在 FAILFAST 模式下输出错误。自 Spark 2.4 起,CSV 行只有在包含从 CSV 数据源请求的格式错误的列值时才被视为格式错误,其他值可以被忽略。例如,CSV 文件包含“id,name”头和一行“1234”。在 Spark 2.4 中,id 列的选择由一个列值为 1234 的行组成,而在 Spark 2.3 及更早版本中,该行在 DROPMALFORMED 模式下为空。要恢复之前的行为,将
spark.sql.csv.parser.columnPruning.enabled设置为false。 -
自 Spark 2.4 起,计算统计信息的文件列表默认是并行完成的。这可以通过将
spark.sql.statistics.parallelFileListingInStatsComputation.enabled设置为False来禁用。 -
自 Spark 2.4 起,元数据文件(例如 Parquet 摘要文件)和临时文件在计算统计信息时不作为数据文件进行计数。
-
自 Spark 2.4 起,空字符串以带引号的空字符串
""的形式保存。在 2.3 及更早版本中,空字符串等于null值,并且在保存的 CSV 文件中不反映任何字符。例如,"a", null, "", 1被写为a,,,1。自 Spark 2.4 起,相同的行被保存为a,,"",1。要恢复之前的行为,将 CSV 选项emptyValue设置为空(未带引号)字符串。 -
自 Spark 2.4 起,LOAD DATA 命令支持通配符
?和*,分别匹配任意一个字符和零个或多个字符。例如:LOAD DATA INPATH '/tmp/folder*/'或LOAD DATA INPATH '/tmp/part-?'。例如,space等特殊字符现在也可以在路径中使用。例如:LOAD DATA INPATH '/tmp/folder name/'。 -
在 Spark 2.3 及更早版本中,没有 GROUP BY 的 HAVING 被视为 WHERE。这意味着,
SELECT 1 FROM range(10) HAVING true被执行为SELECT 1 FROM range(10) WHERE true并返回 10 行。这违反了 SQL 标准,并在 Spark 2.4 中得到了修复。自 Spark 2.4 起,没有 GROUP BY 的 HAVING 被视为全局聚合,这意味着SELECT 1 FROM range(10) HAVING true只会返回一行。要恢复之前的行为,将spark.sql.legacy.parser.havingWithoutGroupByAsWhere设置为true。 -
在 2.3 及更早版本中,从 Parquet 数据源表读取时,Spark 始终返回 null 对于在 Hive 元存储模式和 Parquet 模式中大小写不同的列名,无论
spark.sql.caseSensitive是设置为true还是false。自 2.4 起,当spark.sql.caseSensitive设置为false时,Spark 在 Hive 元存储模式和 Parquet 模式之间进行不区分大小写的列名解析,因此即使列名的大小写不同,Spark 也会返回对应的列值。如果存在歧义(即匹配到多个 Parquet 列),则会抛出异常。这一更改也适用于设置为true的 Parquet Hive 表,当spark.sql.hive.convertMetastoreParquet设置为true时。
从 Spark SQL 2.2 升级到 2.3
-
自 Spark 2.3 起,当引用的列仅包括内部的损坏记录列(默认命名为
_corrupt_record)时,不允许对原始 JSON/CSV 文件执行查询。例如,spark.read.schema(schema).json(file).filter($"_corrupt_record".isNotNull).count()和spark.read.schema(schema).json(file).select("_corrupt_record").show()。相反,您可以缓存或保存解析结果,然后发送相同的查询。例如,val df = spark.read.schema(schema).json(file).cache(),然后df.filter($"_corrupt_record".isNotNull).count()。 -
percentile_approx函数以前接受数字类型输入并输出双精度类型结果。现在它支持日期类型、时间戳类型和数字类型作为输入类型。结果类型也更改为与输入类型相同,这对分位数来说更合理。 -
自 Spark 2.3 起,Join/Filter 的确定性谓词如果可能的话,会在第一个非确定性谓词之后的子操作符中下推/透传。在之前的 Spark 版本中,这些过滤器不符合谓词下推的条件。
-
分区列推断以前为不同的推断类型找到错误的公共类型,例如,之前对于双精度类型和日期类型得出的公共类型为双精度类型。现在它为此类冲突找到了正确的公共类型。冲突解决遵循下表:
InputA \ InputB NullType IntegerType LongType DecimalType(38,0)* DoubleType DateType TimestampType StringType
请注意,对于 DecimalType(38,0),上面的表格故意没有涵盖所有其他比例和精度的组合,因为目前我们只推断像 `BigInteger`/`BigInt` 这样的十进制类型。例如,1.1 被推断为双精度类型。
-
自 Spark 2.3 起,当广播哈希连接或广播嵌套循环连接适用时,我们更倾向于广播在广播提示中明确指定的表。有关详细信息,请参见 SQL 查询的连接策略提示 和 SPARK-22489 部分。
-
自 Spark 2.3 起,当所有输入为二进制时,
functions.concat()以二进制格式返回输出。否则,它以字符串格式返回。直到 Spark 2.3,它始终以字符串格式返回,而不管输入类型。要保持旧行为,请将spark.sql.function.concatBinaryAsString设置为true。 -
自 Spark 2.3 起,当所有输入为二进制时,SQL
elt()以二进制格式返回输出。否则,它以字符串格式返回。直到 Spark 2.3,它始终以字符串格式返回,而不管输入类型。要保持旧行为,请将spark.sql.function.eltOutputAsString设置为true。 -
自 Spark 2.3 起,默认情况下,两个小数之间的算术运算如果不能精确表示则返回一个四舍五入的值(而不是返回 NULL)。这符合 SQL ANSI 2011 规范以及 Hive 2.2 中引入的 Hive 新行为(HIVE-15331)。这涉及以下更改
-
算术运算结果类型的确定规则已更新。特别是,如果所需的精度/刻度超出了可用值的范围,则将刻度减少到最大为 6,以防止小数的整数部分被截断。所有算术运算都受到该更改的影响,即加法(
+)、减法(-)、乘法(*)、除法(/)、余数(%)和正模(pmod)。 -
在 SQL 运算中使用的文字值被转换为具有所需的精确度和刻度的 DECIMAL 类型。
-
引入了配置
spark.sql.decimalOperations.allowPrecisionLoss。默认值为true,这意味着这里描述的新行为;如果设置为false,Spark 将使用之前的规则,即它不会调整所需的刻度来表示值,并且如果不能精确表示值,则返回 NULL。
-
-
未别名子查询的语义定义不够明确,行为混淆。自 Spark 2.3 起,我们无效化这种混淆的情况,例如:
SELECT v.i from (SELECT i FROM v),在这种情况下,Spark 将抛出分析异常,因为用户不应在子查询中使用限定符。有关更多详细信息,请参见 SPARK-20690 和 SPARK-21335 。 -
当使用
SparkSession.builder.getOrCreate()创建SparkSession时,如果存在现有的SparkContext,构建器会尝试使用指定给构建器的配置更新现有的SparkContext的SparkConf,但SparkContext是所有SparkSession共享的,因此我们不应更新它们。自 2.3 起,构建器不再更新配置。如果您想更新它们,则需要在创建SparkSession之前更新。
从 Spark SQL 2.1 升级到 2.2
-
Spark 2.1.1 引入了一个新的配置键:
spark.sql.hive.caseSensitiveInferenceMode。它的默认设置为NEVER_INFER,这使得行为与 2.1.0 版保持一致。然而,Spark 2.2.0 将该设置的默认值更改为INFER_AND_SAVE,以恢复与读取底层文件模式具有混合大小写列名的 Hive 元存储表的兼容性。使用INFER_AND_SAVE配置值时,第一次访问时 Spark 将对任何尚未保存推断模式的 Hive 元存储表执行模式推断。请注意,对于具有成千上万的分区的表,模式推断可能是一个非常耗时的操作。如果与混合大小写列名的兼容性不是问题,您可以安全地将spark.sql.hive.caseSensitiveInferenceMode设置为NEVER_INFER,以避免模式推断的初始开销。请注意,在新的默认INFER_AND_SAVE设置下,模式推断的结果将作为元存储键保存以供将来使用。因此,初始模式推断仅在表的第一次访问时发生。 -
自 Spark 2.2.1 和 2.3.0 起,当数据源表的列在分区模式和数据模式中都存在时,模式总是在运行时推断。推断的模式不包含分区列。在读取表时,Spark 尊重这些重叠列的分区值,而不是存储在数据源文件中的值。在 2.2.0 和 2.1.x 版本中,推断的模式是分区的,但表的数据对用户是不可见的(即结果集是空的)。
-
自 Spark 2.2 起,视图定义的存储方式与之前的版本不同。这可能导致 Spark 无法读取之前版本创建的视图。在这种情况下,您需要使用
ALTER VIEW AS或CREATE OR REPLACE VIEW AS在更新的 Spark 版本中重新创建视图。
从 Spark SQL 2.0 升级到 2.1
-
数据源表现在将分区元数据存储在Hive元存储中。这意味着Hive DDL,例如
ALTER TABLE PARTITION ... SET LOCATION现在可用于使用数据源API创建的表。-
可以通过
MSCK REPAIR TABLE命令将遗留数据源表迁移到此格式。建议迁移遗留表,以利用Hive DDL支持和改进的计划性能。 -
要确定表是否已迁移,请在对表发出
DESCRIBE FORMATTED时查找PartitionProvider: Catalog属性。
-
-
数据源表的
INSERT OVERWRITE TABLE ... PARTITION ...行为的变化。-
在先前的Spark版本中,
INSERT OVERWRITE会覆盖整个数据源表,即使给出了分区规范。现在仅覆盖与规范匹配的分区。 -
请注意,这仍然与Hive表的行为不同,后者仅覆盖与新插入数据重叠的分区。
-
从 Spark SQL 1.6 升级到 2.0
-
SparkSession现在是 Spark 的新入口点,取代了旧的SQLContext和HiveContext。请注意,旧的 SQLContext 和 HiveContext 为了向后兼容而保留。通过SparkSession可以访问新的catalog接口 - 对数据库和表的现有 API 可通过listTables、createExternalTable、dropTempView、cacheTable等进行访问。 -
数据集 API 和 DataFrame API 被统一。在 Scala 中,
DataFrame成为Dataset[Row]的类型别名,而 Java API 用户必须用Dataset替代DataFrame。类型转换(例如,map、filter和groupByKey)和非类型转换(例如,select和groupBy)都可以在数据集类上使用。由于 Python 和 R 中的编译时类型安全不是语言特性,数据集的概念不适用于这些语言的 API。相反,DataFrame保持为主要的编程抽象,这与这些语言中的单节点数据框的概念相似。 -
数据集和 DataFrame API
unionAll已被弃用,已被union取代。 -
数据集和 DataFrame API
explode已被弃用,建议使用functions.explode()和select或flatMap。 -
数据集和 DataFrame API
registerTempTable已被弃用,并已被createOrReplaceTempView取代。 -
对
CREATE TABLE ... LOCATION行为的更改适用于 Hive 表。-
从 Spark 2.0 开始,
CREATE TABLE ... LOCATION等同于 <code class="language-plaintext high
-
从 Spark SQL 1.5 升级到 1.6
-
从 Spark 1.6 开始,默认情况下,Thrift 服务器以多会话模式运行。这意味着每个 JDBC/ODBC 连接拥有自己 SQL 配置和临时函数注册表的副本。不过,缓存表仍然是共享的。如果您希望 Thrift 服务器以旧的单会话模式运行,请将选项
spark.sql.hive.thriftServer.singleSession设置为true。您可以将此选项添加到spark-defaults.conf,或者通过--conf将其传递给start-thriftserver.sh:
./sbin/start-thriftserver.sh \
--conf spark.sql.hive.thriftServer.singleSession=true \
...
- 从 Spark 1.6 开始,LongType 转换为 TimestampType 期望的是秒而不是微秒。此更改是为了与 Hive 1.2 的行为相匹配,以便在从数值类型转换为 TimestampType 时实现更一致的类型转换。有关详细信息,请参见 SPARK-11724 。
从 Spark SQL 1.4 升级到 1.5
-
现在默认启用手动管理内存(Tungsten)的优化执行,以及表达式评估的代码生成。这些功能可以通过将
spark.sql.tungsten.enabled设置为false来禁用。 -
Parquet 架构合并现在不再默认启用。可以通过将
spark.sql.parquet.mergeSchema设置为true来重新启用。 -
内存列存储的分区修剪默认为启用。可以通过将
spark.sql.inMemoryColumnarStorage.partitionPruning设置为false来禁用。 -
不再支持无限精度的小数列,而是 Spark SQL 强制最大精度为 38。当从
BigDecimal对象推断模式时,现在使用(38, 18)的精度。当在 DDL 中未指定精度时,默认保持为Decimal(10, 0)。 -
时间戳现在以 1 微秒的精度存储,而不是 1 纳秒。
-
在
sql方言中,浮点数现在被解析为小数。HiveQL 解析保持不变。 -
SQL/DataFrame 函数的规范名称现在为小写(例如,sum vs SUM)。
-
JSON 数据源不会自动加载其他应用程序创建的新文件(即,未通过 Spark SQL 插入数据集的文件)。对于 JSON 持久表(即表的元数据存储在 Hive Metastore 中),用户可以使用
REFRESH TABLESQL 命令或HiveContext的refreshTable方法 将这些新文件包含到表中。对于表示 JSON 数据集的 DataFrame,用户需要重新创建 DataFrame,新 DataFrame 将包含新文件。
从 Spark SQL 1.3 升级到 1.4
数据框数据读写接口
根据用户反馈,我们创建了一个新的、更流畅的API用于读取数据 (
SQLContext.read
)
和写入数据 (
DataFrame.write
),
并弃用了旧的API (例如,
SQLContext.parquetFile
,
SQLContext.jsonFile
).
请参阅
SQLContext.read
的API文档 (
Scala
,
Java
,
Python
) 和
DataFrame.write
的更多信息 (
Scala
,
Java
,
Python
)。
DataFrame.groupBy 保留分组列
根据用户反馈,我们更改了
DataFrame.groupBy().agg()
的默认行为,以保留结果中的分组列
DataFrame
。要保持1.3中的行为,请将
spark.sql.retainGroupColumns
设置为
false
。
import pyspark.sql.functions as func
# 在 1.3.x 中,为了让分组列 "department" 显示,
# 它必须作为 agg 函数调用的一部分明确包含。
df.groupBy("department").agg(df["department"], func.max("age"), func.sum("expense"))
# 在 1.4+ 中,分组列 "department" 会自动包含。
df.groupBy("department").agg(func.max("age"), func.sum("expense"))
# 通过以下方式恢复到 1.3.x 行为(不保留分组列):
sqlContext.setConf("spark.sql.retainGroupColumns", "false")
// 在1.3.x中,为了使分组列“department”显示出来,
// 它必须作为agg函数调用的一部分显式包含。
df.groupBy("department").agg($"department", max("age"), sum("expense"))
// 在1.4+中,分组列“department”会自动包含。
df.groupBy("department").agg(max("age"), sum("expense"))
// 通过下面的方式恢复到1.3行为(不保留分组列):
sqlContext.setConf("spark.sql.retainGroupColumns", "false")
// 在 1.3.x 中,为了使分组列“department”能够显示,
// 必须将其显式包含在 agg 函数调用中。
df.groupBy("department").agg(col("department"), max("age"), sum("expense"));
// 在 1.4+ 中,分组列“department”会自动包含。
df.groupBy("department").agg(max("age"), sum("expense"));
// 通过以下方式恢复到 1.3 行为(不保留分组列):
sqlContext.setConf("spark.sql.retainGroupColumns", "false");
在DataFrame.withColumn上的行为变化
在1.4之前,DataFrame.withColumn()仅支持添加一列。即使结果DataFrame中可能存在同名的列,该列也将始终作为具有指定名称的新列添加。自1.4以来,DataFrame.withColumn()支持添加与所有现有列名称不同的列,或替换同名的现有列。
请注意,这一更改仅适用于Scala API,而不适用于PySpark和SparkR。
从 Spark SQL 1.0-1.2 升级到 1.3
在Spark 1.3中,我们从Spark SQL中移除了“Alpha”标签,并作为此的一部分对可用的API进行了清理。从Spark 1.3开始,Spark SQL将与1.X系列中的其他版本提供二进制兼容性。此兼容性保证不包括被明确标记为不稳定的API(即DeveloperAPI或Experimental)。
SchemaRDD重命名为DataFrame
用户在升级到 Spark SQL 1.3 时最明显的变化是
SchemaRDD
被重命名为
DataFrame
。这主要是因为 DataFrame 不再直接从 RDD 继承,而是通过自己的实现提供大部分 RDD 提供的功能。DataFrame 仍然可以通过调用
.rdd
方法转换为 RDD。
在Scala中,有一个类型别名从
SchemaRDD
到
DataFrame
用以提供某些用例的源兼容性。仍然建议用户更新他们的代码以使用
DataFrame
。Java和Python用户需要更新他们的代码。
Java和Scala API的统一
在Spark 1.3之前,有单独的Java兼容类(
JavaSQLContext
和
JavaSchemaRDD
),它们镜像了Scala API。在Spark 1.3中,Java API和Scala API已统一。无论使用哪种语言的用户都应使用
SQLContext
和
DataFrame
。一般来说,这些类试图使用两种语言都能使用的类型(即使用
Array
而不是特定于语言的集合)。在某些情况下,如果没有通用类型(例如,用于传递闭包或Maps)则使用函数重载。
此外,Java特定类型API已经被移除。Scala和Java的用户应该使用
org.apache.spark.sql.types
中的类来以编程方式描述架构。
隐式转换的隔离与dsl包的移除(仅限Scala)
在Spark 1.3之前的许多代码示例中,通常以
import sqlContext._
开头,这将所有函数导入到作用域中。在Spark 1.3中,我们将将
RDD
转换为
DataFrame
的隐式转换隔离到了
SQLContext
内的一个对象中。用户现在应该编写
import sqlContext.implicits._
。
此外,隐式转换现在仅增加由
Product
(即,案例类或元组)组成的 RDD 的
toDF
方法,而不是自动应用。
在DSL中使用函数时(现在已被
DataFrame
API 替代),用户通常会导入
org.apache.spark.sql.catalyst.dsl
。而应该使用公共数据框函数API:
import org.apache.spark.sql.functions._
。
在org.apache.spark.sql中移除类型别名(仅限Scala)
Spark 1.3 移除了基础 sql 包中与
DataType
相关的类型别名。用户应改为导入
org.apache.spark.sql.types
中的类。
UDF 注册已转移至
sqlContext.udf
(Java & Scala)
用于注册 UDF 的函数,无论是在 DataFrame DSL 还是 SQL 中,都已移入
SQLContext
中的 udf 对象。
sqlContext.udf.register("strLen", (s: String) => s.length())
sqlContext.udf().register("strLen", (String s) -> s.length(), DataTypes.IntegerType);
Python UDF 注册保持不变。
与 Apache Hive 的兼容性
Spark SQL旨在与Hive Metastore、SerDes和UDF兼容。 目前,Hive SerDes和UDF是基于内置的Hive, Spark SQL可以连接到不同版本的Hive Metastore(从0.12.0到2.3.9和3.0.0到3.1.3。另请参见 与不同版本的Hive Metastore交互 )。
在现有Hive仓库中部署
Spark SQL Thrift JDBC 服务器旨在与现有 Hive 安装“开箱即用”兼容。您无需修改现有 Hive Metastore,也不需要改变表的数据位置或分区。
支持的Hive特性
Spark SQL 支持绝大多数 Hive 功能,例如:
-
Hive 查询语句,包括:
-
SELECT -
GROUP BY -
ORDER BY -
DISTRIBUTE BY -
CLUSTER BY -
SORT BY
-
-
所有 Hive 操作符,包括:
-
关系操作符 (
=,<=>,==,<>,<,>,>=,<=, 等) -
算术操作符 (
+,-,*,/,%, 等) -
逻辑操作符 (
AND,OR, 等) - 复杂类型构造函数
-
数学函数 (
sign,ln,cos, 等) -
字符串函数 (
instr,length,printf, 等)
-
关系操作符 (
- 用户自定义函数 (UDF)
- 用户自定义聚合函数 (UDAF)
- 用户自定义序列化格式 (SerDes)
- 窗口函数
-
连接
-
JOIN -
{LEFT|RIGHT|FULL} OUTER JOIN -
LEFT SEMI JOIN -
LEFT ANTI JOIN -
CROSS JOIN
-
- 并集
-
子查询
-
FROM 子句中的子查询
SELECT col FROM (SELECT a + b AS col FROM t1) t2 -
WHERE 子句中的子查询
-
WHERE 子句中的相关或非相关 IN 和 NOT IN 语句
SELECT col FROM t1 WHERE col IN (SELECT a FROM t2 WHERE t1.a = t2.a) SELECT col FROM t1 WHERE col IN (SELECT a FROM t2) -
WHERE 子句中的相关或非相关 EXISTS 和 NOT EXISTS 语句
SELECT col FROM t1 WHERE EXISTS (SELECT t2.a FROM t2 WHERE t1.a = t2.a AND t2.a > 10) SELECT col FROM t1 WHERE EXISTS (SELECT t2.a FROM t2 WHERE t2.a > 10) -
JOIN 条件中的非相关 IN 和 NOT IN 语句
SELECT t1.col FROM t1 JOIN t2 ON t1.a = t2.a AND t1.a IN (SELECT a FROM t3) -
JOIN 条件中的非相关 EXISTS 和 NOT EXISTS 语句
SELECT t1.col FROM t1 JOIN t2 ON t1.a = t2.a AND EXISTS (SELECT * FROM t3 WHERE t3.a > 10)
-
-
- 抽样
- 解释
- 分区表,包括动态分区插入
-
视图
-
如果在视图定义查询中没有指定列别名,Spark 和 Hive 将生成别名,但方式不同。为了让 Spark 能够读取由 Hive 创建的视图,用户应该在视图定义查询中明确指定列别名。比如,Spark 不能读取以下由 Hive 创建的
v1。CREATE VIEW v1 AS SELECT * FROM (SELECT c + 1 FROM (SELECT 1 c) t1) t2;相反,你应将
v1创建为以下,以显式指定列别名。CREATE VIEW v1 AS SELECT * FROM (SELECT c + 1 AS inc_c FROM (SELECT 1 c) t1) t2;
-
-
所有 Hive DDL 函数,包括:
-
CREATE TABLE -
CREATE TABLE AS SELECT -
CREATE TABLE LIKE -
ALTER TABLE
-
-
大多数 Hive 数据类型,包括:
-
TINYINT -
SMALLINT -
INT -
BIGINT -
BOOLEAN -
FLOAT -
DOUBLE -
STRING -
BINARY -
TIMESTAMP -
DATE -
ARRAY<> -
MAP<> -
STRUCT<>
-
不支持的Hive功能
下面是我们尚未支持的Hive特性列表。大多数这些特性在Hive部署中很少使用。
深奥的Hive特性
-
UNION类型 - 唯一连接
- 列统计收集:Spark SQL 目前不支持利用扫描收集列统计信息,仅支持填充 hive 元存储的 sizeInBytes 字段。
Hive 输入/输出格式
- CLI的文件格式:对于返回到CLI的结果,Spark SQL只支持TextOutputFormat。
- Hadoop档案
Hive 优化
一些Hive优化尚未包含在Spark中。其中一些(例如索引)由于Spark SQL的内存计算模型而重要性较低。其他一些则计划在未来的Spark SQL版本中发布。
- 块级位图索引和虚拟列(用于构建索引)
-
自动确定连接和分组的减速器数量:目前,在Spark SQL中,您需要使用“
SET spark.sql.shuffle.partitions=[num_tasks];”控制shuffle后的并行度。 - 仅元数据查询:对于仅可以通过使用元数据回答的查询,Spark SQL仍然启动任务来计算结果。
- 倾斜数据标志:Spark SQL不遵循Hive中的倾斜数据标志。
-
STREAMTABLE提示在连接中:Spark SQL不遵循STREAMTABLE提示。 - 合并多个小文件以进行查询结果:如果结果输出包含多个小文件,Hive可以选择将小文件合并为较少的大文件,以避免溢出HDFS元数据。Spark SQL不支持此功能。
Hive 用户自定义函数/用户自定义表函数/用户自定义聚合函数
并不是所有的 Hive UDF/UDTF/UDAF 的 API 都被 Spark SQL 支持。以下是未支持的 API:
-
getRequiredJars和getRequiredFiles(UDF和GenericUDF) 是一个函数,用于自动包含此 UDF 所需的额外资源。 -
initialize(StructObjectInspector)在GenericUDTF中尚不支持。目前 Spark SQL 仅使用一个已弃用的接口initialize(ObjectInspector[])。 -
configure(GenericUDF、GenericUDTF和GenericUDAFEvaluator) 是一个函数,用于用MapredContext初始化函数,这对 Spark 不适用。 -
close(GenericUDF和GenericUDAFEvaluator) 是一个函数,用于释放相关资源。Spark SQL 在任务完成时并不会调用此函数。 -
reset(GenericUDAFEvaluator) 是一个函数,用于重新初始化聚合以重用相同的聚合。Spark SQL 当前不支持聚合的重用。 -
getWindowingEvaluator(GenericUDAFEvaluator) 是一个函数,通过在固定窗口上评估聚合来优化聚合。
不兼容的 Hive UDF
以下是Hive和Spark生成不同结果的场景:
-
SQRT(n)如果 n < 0,Hive 返回 null, Spark SQL 返回 NaN。 -
ACOS(n)如果 n < -1 或 n > 1,Hive 返回 null, Spark SQL 返回 NaN。 -
ASIN(n)如果 n < -1 或 n > 1,Hive 返回 null, Spark SQL 返回 NaN。 -
CAST(n AS TIMESTAMP)如果 n 是整数,Hive 将 n 视为毫秒,Spark SQL 将 n 视为秒。