迁移指南:SQL、Datasets 和 DataFrame
- 从 Spark SQL 4.0 升级到 4.1
- 从 Spark SQL 3.5 升级到 4.0
- 从 Spark SQL 3.5.3 升级到 3.5.4
- 从 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 4.0 升级到 4.1
- 自 Spark 4.1 起,如果 Parquet 文件中缺少所有请求的字段,Parquet 读取器不再默认假设所有结构(struct)值为 null。新的默认行为是读取文件中存在的额外结构字段来确定非空性。要恢复之前的行为,请将
spark.sql.legacy.parquet.returnNullStructIfAllFieldsMissing设置为true。 - 自 Spark 4.1 起,Spark Thrift Server 在 GetColumns 操作的结果中返回正确的 1-based ORDINAL_POSITION,而不是之前的错误 0-based。要恢复之前的行为,请将
spark.sql.legacy.hive.thriftServer.useZeroBasedColumnOrdinalPosition设置为true。
从 Spark SQL 3.5 升级到 4.0
- 自 Spark 4.0 起,
spark.sql.ansi.enabled默认开启。要恢复之前的行为,请将spark.sql.ansi.enabled设置为false,或将SPARK_ANSI_SQL_MODE设置为false。 - 自 Spark 4.0 起,不带
USING和STORED AS的CREATE TABLE语法将使用spark.sql.sources.default的值作为表提供程序,而不是Hive。要恢复之前的行为,请将spark.sql.legacy.createHiveTableByDefault设置为true,或将SPARK_SQL_LEGACY_CREATE_HIVE_TABLE设置为true。 - 自 Spark 4.0 起,插入 map 元素时的默认行为改为先将 -0.0 键规范化为 0.0。受影响的 SQL 函数包括
create_map、map_from_arrays、map_from_entries和map_concat。要恢复之前的行为,请将spark.sql.legacy.disableMapKeyNormalization设置为true。 - 自 Spark 4.0 起,
spark.sql.maxSinglePartitionBytes的默认值从Long.MaxValue改为128m。要恢复之前的行为,请将spark.sql.maxSinglePartitionBytes设置为9223372036854775807(Long.MaxValue)。 - 自 Spark 4.0 起,任何对 SQL 表的读取都会考虑 SQL 配置
spark.sql.files.ignoreCorruptFiles/spark.sql.files.ignoreMissingFiles,而不是核心配置spark.files.ignoreCorruptFiles/spark.files.ignoreMissingFiles。 - 自 Spark 4.0 起,当读取 SQL 表遇到
org.apache.hadoop.security.AccessControlException和org.apache.hadoop.hdfs.BlockMissingException时,即使spark.sql.files.ignoreCorruptFiles设置为true,异常也会被抛出并导致任务失败。 - 自 Spark 4.0 起,
spark.sql.hive.metastore不再支持 2.0.0 之前的 Hive,因为它们需要 JDK 8,而 Spark 已不再支持。用户应迁移到更高版本。 - 自 Spark 4.0 起,Spark 移除了
hive-llap-common依赖。要恢复之前的行为,请将hive-llap-commonjar 添加到类路径中。 - 自 Spark 4.0 起,
spark.sql.parquet.compression.codec不再支持编解码器名称lz4raw,请改用lz4_raw。 - 自 Spark 4.0 起,在非 ANSI 模式下将时间戳强制转换为 byte/short/int 发生溢出时,Spark 将返回 null 而不是包裹后的值。
- 自 Spark 4.0 起,
encode()和decode()函数仅支持以下字符集:‘US-ASCII’、‘ISO-8859-1’、‘UTF-8’、‘UTF-16BE’、‘UTF-16LE’、‘UTF-16’、‘UTF-32’。要恢复之前函数接受 Spark 当前所用 JDK 支持的字符集行为,请将spark.sql.legacy.javaCharsets设置为true。 - 自 Spark 4.0 起,
encode()和decode()函数在处理不可映射字符时会引发MALFORMED_CHARACTER_CODING错误,而在 Spark 3.5 及更早版本中,这些字符会被替换为乱码。要恢复之前的行为,请将spark.sql.legacy.codingErrorAction设置为true。例如,如果您尝试使用 ‘UTF-8’decode一个字符串值tést/ [116, -23, 115, 116] (以 latin1 编码),您会得到tst。 - 自 Spark 4.0 起,删除了带有
spark.sql.legacy前缀的旧版日期时间重置 SQL 配置。要恢复之前的行为,请使用以下配置spark.sql.parquet.int96RebaseModeInWrite代替spark.sql.legacy.parquet.int96RebaseModeInWritespark.sql.parquet.datetimeRebaseModeInWrite代替spark.sql.legacy.parquet.datetimeRebaseModeInWritespark.sql.parquet.int96RebaseModeInRead代替spark.sql.legacy.parquet.int96RebaseModeInReadspark.sql.avro.datetimeRebaseModeInWrite代替spark.sql.legacy.avro.datetimeRebaseModeInWritespark.sql.avro.datetimeRebaseModeInRead代替spark.sql.legacy.avro.datetimeRebaseModeInRead
- 自 Spark 4.0 起,
spark.sql.orc.compression.codec的默认值从snappy改为zstd。要恢复之前的行为,请将spark.sql.orc.compression.codec设置为snappy。 - 自 Spark 4.0 起,SQL 配置
spark.sql.legacy.allowZeroIndexInFormatString已弃用。考虑将format_string函数的strfmt改为使用 1-based 索引。第一个参数必须由1$引用,第二个由2$引用,依此类推。 - 自 Spark 4.0 起,Postgres JDBC 数据源将读取 JDBC TIMESTAMP WITH TIME ZONE 为 TimestampType,而不管 JDBC 读取选项
preferTimestampNTZ如何;而在 3.5 及更早版本中,当preferTimestampNTZ=true时读取为 TimestampNTZType。要恢复之前的行为,请将spark.sql.legacy.postgres.datetimeMapping.enabled设置为true。 - 自 Spark 4.0 起,Postgres JDBC 数据源将写入 TimestampType 为 TIMESTAMP WITH TIME ZONE,而在 3.5 及更早版本中,它写入为 TIMESTAMP(即 TIMESTAMP WITHOUT TIME ZONE)。要恢复之前的行为,请将
spark.sql.legacy.postgres.datetimeMapping.enabled设置为true。 - 自 Spark 4.0 起,MySQL JDBC 数据源将读取 TIMESTAMP 为 TimestampType,而不管 JDBC 读取选项
preferTimestampNTZ如何;而在 3.5 及更早版本中,当preferTimestampNTZ=true时读取为 TimestampNTZType。要恢复之前的行为,请将spark.sql.legacy.mysql.timestampNTZMapping.enabled设置为true,MySQL DATETIME 不受影响。 - 自 Spark 4.0 起,MySQL JDBC 数据源将读取 SMALLINT 为 ShortType,而在 Spark 3.5 及更早版本中,它被读取为 IntegerType。MEDIUMINT UNSIGNED 被读取为 IntegerType,而在 Spark 3.5 及更早版本中,它被读取为 LongType。要恢复之前的行为,您可以将列转换为旧类型。
- 自 Spark 4.0 起,MySQL JDBC 数据源将读取 FLOAT 为 FloatType,而在 Spark 3.5 及更早版本中,它被读取为 DoubleType。要恢复之前的行为,您可以将列转换为旧类型。
- 自 Spark 4.0 起,MySQL JDBC 数据源将读取 BIT(n > 1) 为 BinaryType,而在 Spark 3.5 及更早版本中,读取为 LongType。要恢复之前的行为,请将
spark.sql.legacy.mysql.bitArrayMapping.enabled设置为true。 - 自 Spark 4.0 起,MySQL JDBC 数据源将写入 ShortType 为 SMALLINT,而在 Spark 3.5 及更早版本中,写入为 INTEGER。要恢复之前的行为,您可以在写入之前将列替换为 IntegerType。
- 自 Spark 4.0 起,MySQL JDBC 数据源将写入 TimestampNTZType 为 MySQL DATETIME,因为它们都表示 TIMESTAMP WITHOUT TIME ZONE,而在 3.5 及更早版本中,它写入为 MySQL TIMESTAMP。要恢复之前的行为,请将
spark.sql.legacy.mysql.timestampNTZMapping.enabled设置为true。 - 自 Spark 4.0 起,Oracle JDBC 数据源将写入 TimestampType 为 TIMESTAMP WITH LOCAL TIME ZONE,而在 Spark 3.5 及更早版本中,写入为 TIMESTAMP。要恢复之前的行为,请将
spark.sql.legacy.oracle.timestampMapping.enabled设置为true。 - 自 Spark 4.0 起,MsSQL Server JDBC 数据源将读取 TINYINT 为 ShortType,而在 Spark 3.5 及更早版本中,读取为 IntegerType。要恢复之前的行为,请将
spark.sql.legacy.mssqlserver.numericMapping.enabled设置为true。 - 自 Spark 4.0 起,MsSQL Server JDBC 数据源将读取 DATETIMEOFFSET 为 TimestampType,而在 Spark 3.5 及更早版本中,读取为 StringType。要恢复之前的行为,请将
spark.sql.legacy.mssqlserver.datetimeoffsetMapping.enabled设置为true。 - 自 Spark 4.0 起,DB2 JDBC 数据源将读取 SMALLINT 为 ShortType,而在 Spark 3.5 及更早版本中,它被读取为 IntegerType。要恢复之前的行为,请将
spark.sql.legacy.db2.numericMapping.enabled设置为true。 - 自 Spark 4.0 起,DB2 JDBC 数据源将写入 BooleanType 为 BOOLEAN,而在 Spark 3.5 及更早版本中,写入为 CHAR(1)。要恢复之前的行为,请将
spark.sql.legacy.db2.booleanMapping.enabled设置为true。 - 自 Spark 4.0 起,
spark.sql.legacy.ctePrecedencePolicy的默认值已从EXCEPTION改为CORRECTED。内部 CTE 定义优先于外部定义,而不是引发错误。 - 自 Spark 4.0 起,
spark.sql.legacy.timeParserPolicy的默认值已从EXCEPTION改为CORRECTED。如果启用了 ANSI 模式,将引发CANNOT_PARSE_TIMESTAMP,而不是引发INCONSISTENT_BEHAVIOR_CROSS_VERSION错误。如果禁用了 ANSI 模式,将返回NULL。请参阅 日期时间格式化和解析模式。 - 自 Spark 4.0 起,修复了一个错误,该错误错误地允许在
!不是前缀运算符时使用!代替NOT。像expr ! IN (...)、expr ! BETWEEN ...或col ! NULL这样的子句现在会引发语法错误。要恢复之前的行为,请将spark.sql.legacy.bangEqualsNot设置为true。 - 自 Spark 4.0 起,默认情况下,视图容忍查询中的列类型更改并使用强制转换进行补偿。要恢复之前仅允许向上转换的行为,请将
spark.sql.legacy.viewSchemaCompensation设置为false。 - 自 Spark 4.0 起,视图允许控制它们对基础查询更改的反应方式。默认情况下,视图容忍查询中的列类型更改并使用强制转换进行补偿。要禁用此功能,请将
spark.sql.legacy.viewSchemaBindingMode设置为false。这也从DESCRIBE EXTENDED和SHOW CREATE TABLE中删除了该子句。 - 自 Spark 4.0 起,存储分区连接功能标志
spark.sql.sources.v2.bucketing.pushPartValues.enabled被设置为true。要恢复之前的行为,请将spark.sql.sources.v2.bucketing.pushPartValues.enabled设置为false。 - 自 Spark 4.0 起,当
language参数不为NULL且country参数为NULL时,sentences函数使用Locale(language)而不是Locale.US。 - 自 Spark 4.0 起,从文件源表读取将正确遵守查询选项,例如分隔符。之前,第一个查询计划会被缓存,后续选项更改会被忽略。要恢复之前的行为,请将
spark.sql.legacy.readFileSourceTableCacheIgnoreOptions设置为true。
从 Spark SQL 3.5.3 升级到 3.5.4
- 自 Spark 3.5.4 起,当读取 SQL 表遇到
org.apache.hadoop.security.AccessControlException和org.apache.hadoop.hdfs.BlockMissingException时,即使spark.sql.files.ignoreCorruptFiles设置为true,异常也会被抛出并导致任务失败。
从 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 Server 将在取消正在运行的语句时中断任务。要恢复之前的行为,请将
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-based。它将新元素插入输入数组末尾(索引 -1)。要恢复之前的行为,请将spark.sql.legacy.negativeIndexInArrayInsert设置为true。 - 自 Spark 3.5 起,Avro 在读取 Interval 类型作为 Date 或 Timestamp 类型,或者读取具有较低精度的 Decimal 类型时,将抛出
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 在获取具有不存在键的 map 值时始终返回 NULL 结果。在 Spark 3.3 或更早版本中,会报错。 - 自 Spark 3.4 起,SQL CLI
spark-sql在AnalysisException的错误消息之前不再打印前缀Error in query:。 - 自 Spark 3.4 起,当
regex参数为空时,split函数忽略末尾的空字符串。 - 自 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的行为,如果类型转换失败,可能会导致异常,例如,如果列p是 int 类型,则ALTER TABLE .. ADD PARTITION(p='a')。要恢复旧版行为,请将spark.sql.legacy.skipTypeValidationOnAlterPartition设置为true。 - 自 Spark 3.4 起,默认启用嵌套数据类型(array、map 和 struct)的向量化读取器。要恢复旧版行为,请将
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 起,当
spark.sql.legacy.allowNonEmptyLocationInCTAS设置为true时,CREATE TABLE AS SELECT ...的行为已从 OVERWRITE 更改为 APPEND。建议用户避免在非空表位置进行 CTAS。
从 Spark SQL 3.2 升级到 3.3
-
自 Spark 3.3 起,Spark SQL 中的
histogram_numeric函数返回一个 struct 数组 (x, y) 类型的输出,其中返回值中 ‘x’ 字段的类型从聚合函数中消耗的输入值中传播。在 Spark 3.2 或更早版本中,‘x’ 总是具有 double 类型。您可以自 Spark 3.3 起选择使用配置spark.sql.legacy.histogramNumericPropagateInputType以恢复到之前的行为。 -
自 Spark 3.3 起,Spark SQL 中的
DayTimeIntervalType在ArrowWriter和ArrowColumnVector开发人员 API 中映射为 Arrow 的Duration类型。此前,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 会将
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 起,
format_string(strfmt, obj, ...)和printf(strfmt, obj, ...)中的strfmt将不再支持使用0$指定第一个参数,当使用参数索引指示参数在参数列表中的位置时,第一个参数应始终由1$引用。 -
自 Spark 3.3 起,null 在 CSV 数据源中默认写入为空字符串。在 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 起,在读取定义为
FloatType或DoubleType的 JSON 属性值时,字符串"+Infinity"、"+INF"和"-INF"现在会被解析为相应的值,此外还支持已有的"Infinity"和"-Infinity"变体。此更改旨在提高与 Jackson 对这些值的非引号版本解析的一致性。此外,allowNonNumericNumbers选项现在受到尊重,因此如果禁用了此选项,这些字符串将被视为无效。 -
自 Spark 3.3 起,Spark 将尝试使用内置数据源写入器而不是
INSERT OVERWRITE DIRECTORY中的 Hive serde。此行为仅在分别针对 Parquet 和 ORC 格式启用了spark.sql.hive.convertMetastoreParquet或spark.sql.hive.convertMetastoreOrc时有效。要恢复 Spark 3.3 之前的行为,您可以将spark.sql.hive.convertMetastoreInsertDir设置为false。 -
自 Spark 3.3 起,round 类函数的返回类型精度已修复。这可能会导致 Spark 在使用旧版本创建的视图时抛出
CANNOT_UP_CAST_DATATYPE错误类的AnalysisException。在这种情况下,您需要使用较新的 Spark 版本通过 ALTER VIEW AS 或 CREATE OR REPLACE VIEW AS 重新创建视图。 -
自 Spark 3.3 起,
unbase64函数在输入str格式错误时抛出错误。使用try_to_binary(<str>, 'base64')可容忍格式错误的输入并返回 NULL。在 Spark 3.2 及更早版本中,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 起,不支持读取时间戳类型具有纳秒精度的 Parquet 文件(
INT64 (TIMESTAMP(NANOS, true)))。要恢复 Spark 3.2 之前的行为,您可以将spark.sql.legacy.parquet.nanosAsLong设置为true。 -
在 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 中,脚本转换默认 FIELD DELIMIT 对于无 serde 模式为
\u0001;当用户指定 serde 时,serde 属性field.delim对于 Hive serde 模式为\t。在 Spark 3.1 或更早版本中,默认 FIELD DELIMIT 为\t,当用户指定 serde 时,serde 属性field.delim对于 Hive serde 模式为\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 PARTITIONALTER TABLE .. RENAME PARTITIONALTER TABLE .. DROP PARTITIONALTER TABLE .. RECOVER PARTITIONSMSCK REPAIR TABLELOAD DATAREFRESH TABLETRUNCATE 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'(这是一个非法的日期值),我们最后添加一个空值分区。 -
在 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运算符支持没有 Hive SerDe 的ArrayType/MapType/StructType,在此模式下,我们使用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 中,发送到
QueryExecutionListener时,由DataFrameWriter触发的查询执行始终被命名为command。在 Spark 3.1 及更早版本中,名称为save、insertInto、saveAsTable之一。 -
在 Spark 3.2 中,
allowMissingColumns设置为 true 的Dataset.unionByName会将缺失的嵌套字段添加到 struct 的末尾。在 Spark 3.1 中,嵌套 struct 字段按字母顺序排序。 -
在 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() 返回 long 值。在 Spark 3.0 及更早版本中,此函数返回 int 值。要恢复 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 中,struct 和 map 在转换为字符串时被
{}括号包裹。例如,show()操作和CAST表达式使用此类括号。在 Spark 3.0 及更早版本中,[]括号用于相同目的。要恢复 Spark 3.1 之前的行为,您可以将spark.sql.legacy.castComplexTypesToString.enabled设置为true。 -
在 Spark 3.1 中,结构、数组和 map 的 NULL 元素在转换为字符串时被转换为“null”。在 Spark 3.0 或更早版本中,NULL 元素被转换为空字符串。要恢复 Spark 3.1 之前的行为,您可以将
spark.sql.legacy.castComplexTypesToString.enabled设置为true。 -
在 Spark 3.1 中,当
spark.sql.ansi.enabled为 false 时,如果 decimal 类型列的和溢出,Spark 始终返回 null。在 Spark 3.0 或更早版本中,在这种情况下,decimal 类型列的和可能返回 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。您需要将您的自定义 SerDe 迁移到 Hive 2.3。有关详细信息,请参阅 HIVE-15167。
-
在 Spark 3.1 中,如果时间戳早于 1900-01-01 00:00:00Z 且作为 INT96 类型加载(保存),则从/向 Parquet 文件加载和保存时间戳会失败。在 Spark 3.0 中,操作不会失败,但可能会因从 Julian 到/从 Proleptic Gregorian 日历的重置而导致输入时间戳的偏移。要恢复 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 中,创建或修改永久视图(permanent view)时会捕获运行时的 SQL 配置并将其存储为视图属性。这些配置将应用于视图解析的解析(parsing)和分析(analysis)阶段。要恢复 Spark 3.1 之前的行为,可以将
spark.sql.legacy.useCurrentConfigsForView设置为true。 -
在 Spark 3.1 中,临时视图(temporary view)将与永久视图具有相同的行为,即捕获并存储运行时 SQL 配置、SQL 文本、目录(catalog)和命名空间(namespace)。捕获的视图属性将在视图解析的解析和分析阶段应用。要恢复 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 开始,表模式(table schema)中支持 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 中,对于来自 Hive 外部目录的表,在以下情况下
AnalysisException被其子类取代: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
Dataset/DataFrame 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 中的字段向上转型(up cast)为目标 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 插入到 byte 类型的字段中,结果为 1。该行为由选项spark.sql.storeAssignmentPolicy控制,默认值为 “ANSI”。将该选项设置为 “Legacy” 可恢复之前的行为。 -
ADD JAR命令以前返回一个包含单个值 0 的结果集。现在它返回一个空结果集。 -
Spark 2.4 及以下版本:
SET命令即使在指定键为SparkConf条目时也能工作且没有任何警告,因为它不会更新SparkConf,因此不会产生任何影响,但这种行为可能会混淆用户。在 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 'any comment' LOCATION 'some path'。你可以将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 中,非 Hive-Serde 表中不允许使用 CHAR 类型的列,如果检测到 CHAR 类型,CREATE/ALTER TABLE 命令将失败。请改用 STRING 类型。在 Spark 2.4 及以下版本中,CHAR 类型被视为 STRING 类型,长度参数会被忽略。
UDF 和内置函数
-
在 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 及以下版本中,如果第二个参数是分数或字符串值,它会被强制转换为 int 值,结果是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 记录时。例如,模式为a INT的 JSON 字符串{"a" 1}在以前的版本中被转换为null,但 Spark 3.0 将其转换为Row(null)。 -
在 Spark 2.4 及以下版本中,你可以通过内置函数(如
CreateMap,MapFromArrays等)创建具有 map 类型键的 map 值。在 Spark 3.0 中,不允许使用这些内置函数创建具有 map 类型键的 map 值。用户可以使用map_entries函数将 map 转换为 array<struct<key, value>> 作为解决方法。此外,用户仍然可以从数据源或 Java/Scala 集合中读取具有 map 类型键的 map 值,尽管不鼓励这样做。 -
在 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一致。在几乎所有情况下,这都不会对返回值产生差异,差异非常小,但在 x86 平台上的java.lang.Math在例如log(3.0)这样的情况下可能不会完全匹配,其值在Math.log()和StrictMath.log()之间变化。 -
在 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)值转换为字符串类型时,没有 “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 <table>或FROM <table> UNION ALL FROM <table>的 SQL 查询。在 Hive 风格的FROM <table> SELECT <expr>中,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 中,日-时(day-time)间隔字符串根据
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 中,默认不允许负的小数比例(scale),例如
1E10BD等字面量的数据类型是DecimalType(11, 0)。在 Spark 2.4 及以下版本中,它是DecimalType(2, -9)。要恢复 Spark 3.0 之前的行为,可以将spark.sql.legacy.allowNegativeScaleOfDecimal设置为true。 -
在 Spark 3.0 中,一元算术运算符加(
+)仅接受字符串、数值和间隔类型的值作为输入。此外,带有整数字符串表示的+被强制转换为 double 值,例如,+'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 列引用,并且在 Spark 中df1("a")与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 中,此错误已修复。例如,
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 中,在解析、格式化和转换日期和时间戳以及提取年份、天数等子组件时使用 Proleptic Gregorian 日历。Spark 3.0 使用
java.time包中的 Java 8 API 类,这些类基于 ISO 年表。在 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 实现。新实现对其输入执行严格检查。例如,如果模式是yyyy-MM-dd,则无法解析2015-07-22 10:00:00时间戳,因为解析器不会消耗整个输入。另一个例子是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 值。转换基于 Proleptic Gregorian 日历和 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。之前将Date/Timestamp转换为String的行为可以通过将spark.sql.legacy.typeCoercion.datetimeToString.enabled设置为true来恢复。 - 在 Spark 3.0 中,在从字符串转换为日期和时间戳时支持特殊值。这些值只是简单的符号速记,在读取时被转换为普通的日期或时间戳值。支持以下日期字符串值:
epoch [zoneId]- 1970-01-01today [zoneId]- 在spark.sql.session.timeZone指定的时区中的当前日期yesterday [zoneId]- 当前日期 - 1tomorrow [zoneId]- 当前日期 + 1now- 运行当前查询的日期。它与 today 具有相同的概念
例如
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表达式从日期/时间戳值中提取秒字段时,结果将是一个DecimalType(8, 6)值,其中秒部分有 2 位数字,小数部分有 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 会推断实际的文件模式并更新元存储(metastore)中的表模式。在 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 数据源的解析器将空字符串视为某些数据类型(如
IntegerType)的 null。对于FloatType,DoubleType,DateType和TimestampType,它对空字符串失败并抛出异常。Spark 3.0 不允许空字符串,并将对除StringType和BinaryType之外的数据类型抛出异常。允许空字符串的旧行为可以通过将spark.sql.legacy.json.allowEmptyString.enabled设置为true来恢复。 -
在 Spark 2.4 及以下版本中,当指定模式为
StructType时,JSON 数据源和from_json等 JSON 函数在 PERMISSIVE 模式下将损坏的 JSON 记录转换为所有值为null的行。在 Spark 3.0 中,如果某些 JSON 列值被成功解析并转换为所需类型,返回的行可以包含非null的字段。 -
在 Spark 3.0 中,JSON 数据源和 JSON 函数
schema_of_json如果字符串值匹配 JSON 选项timestampFormat定义的模式,则会从字符串值中推断 TimestampType。将 JSON 选项inferTimestamp设置为false以禁用此类类型推断。 -
在 Spark 2.4 及以下版本中,CSV 数据源在 PERMISSIVE 模式下将格式错误的 CSV 字符串转换为所有值为
null的行。在 Spark 3.0 中,如果某些 CSV 列值被成功解析并转换为所需类型,返回的行可以包含非null的字段。 -
在 Spark 3.0 中,当使用用户提供的模式写入 Avro 文件时,字段通过 catalyst 模式和 Avro 模式之间的字段名称而不是位置进行匹配。
-
在 Spark 3.0 中,当使用用户提供的非空(non-nullable)模式写入 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 configuration中找不到,则回退到SparkContext的Hadoop configuration中存在的hive-site.xml文件。 -
在 Spark 3.0 中,我们为
spark-sql接口将十进制数字用尾随零填充到列的比例,例如查询 Spark 2.4 Spark 3.0 SELECT CAST(1 AS decimal(38, 18));1 1.000000000000000000 -
在 Spark 3.0 中,我们将内置 Hive 从 1.2 升级到 2.3,并带来了以下影响:
-
你可能需要根据要连接的 Hive metastore 版本设置
spark.sql.hive.metastore.version和spark.sql.hive.metastore.jars。例如:如果你的 Hive metastore 版本是 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。 -
当使用 SQL 中的
TRANSFORM运算符进行脚本转换时,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 中,对于来自 Hive 外部目录的表,在以下情况下
AnalysisException被其子类取代。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配置,以便支持旧的 MsSQLServer 方言映射行为,即分别对 SMALLINT 和 REAL JDBC 类型使用 IntegerType 和 DoubleType。要恢复 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 方言分别对 SMALLINT 和 REAL 使用 ShortType 和 FloatType。以前使用 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 中,左参数和右参数分别被提升为 double 类型的数组类型和 double 类型。 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 运算符前面有一个结构(struct)字段时,内部查询也必须包含一个结构字段。在以前的版本中,结构字段会与内部查询的输出进行比较。例如,如果
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 开始,当值为 Jan 01 1970 时,Spark 将表描述列 Last Access 值显示为 UNKNOWN。
-
从 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 开始,将空的 dataframe 写入目录至少会启动一个写任务,即使物理上 dataframe 没有分区。这引入了一个小的行为改变,对于像 Parquet 和 Orc 这样自描述的文件格式,Spark 在写入 0 分区 dataframe 时会在目标目录中创建一个仅包含元数据的文件,以便如果用户稍后读取该目录,模式推断仍然可以工作。这种新行为更合理,且在写入空 dataframe 方面更一致。
-
从 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 等)写入具有空或嵌套空模式的 dataframe。尝试写入具有空模式的 dataframe 时会抛出异常。
-
从 Spark 2.4 开始,Spark 在将 DATE 类型与 TIMESTAMP 类型提升为 TIMESTAMP 后进行比较。将
spark.sql.legacy.compareDateTimestampInTimestamp设置为false可以恢复之前的行为。此选项将在 Spark 3.0 中删除。 -
从 Spark 2.4 开始,不允许创建具有非空位置的受管表。尝试创建具有非空位置的受管表时会抛出异常。将
spark.sql.legacy.allowCreatingManagedTableUsingNonemptyLocation设置为true可以恢复之前的行为。此选项将在 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 向量化。将spark.sql.hive.convertMetastoreOrc设置为false可恢复之前的行为。 -
在 2.3 及更早版本中,如果一行中至少有一个列值格式错误,则 CSV 行被视为格式错误。CSV 解析器在 DROPMALFORMED 模式下删除了此类行,或者在 FAILFAST 模式下输出了错误。从 Spark 2.4 开始,CSV 行仅在包含从 CSV 数据源请求的格式错误的列值时才被视为格式错误,其他值可以忽略。例如,CSV 文件包含 “id,name” 标题和一行 “1234”。在 Spark 2.4 中,id 列的选择由一行一列值 1234 组成,但在 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/'。 -
在 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.sql.caseSensitive设置为true或false,Spark 总是对 Hive metastore 模式和 Parquet 模式中列名大小写不同的任何列返回 null。从 2.4 开始,当spark.sql.caseSensitive设置为false时,Spark 在 Hive metastore 模式和 Parquet 模式之间执行不区分大小写的列名解析,因此即使列名大小写不同,Spark 也会返回相应的列值。如果存在歧义,即匹配了多个 Parquet 列,则会抛出异常。当spark.sql.hive.convertMetastoreParquet设置为true时,此更改也适用于 Parquet Hive 表。
从 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函数以前接受数字类型输入并输出 double 类型结果。现在它支持日期类型、时间戳类型和数字类型作为输入类型。结果类型也更改为与输入类型相同,这对百分位数来说更合理。 -
从 Spark 2.3 开始,如果可能,Join/Filter 中位于第一个非确定性谓词之后的确定性谓词也会被下推/穿过子运算符。在之前的 Spark 版本中,这些过滤器没有资格进行谓词下推。
-
分区列推断以前为不同的推断类型找到了错误的公共类型,例如,以前它最终以 double 类型作为 double 类型和日期类型的公共类型。现在它为这种冲突找到了正确的公共类型。冲突解决遵循下表:
InputA \ InputB NullType IntegerType LongType DecimalType(38,0)* DoubleType DateType TimestampType StringType NullType NullType IntegerType LongType DecimalType(38,0) DoubleType DateType TimestampType StringType IntegerType IntegerType IntegerType LongType DecimalType(38,0) DoubleType StringType StringType StringType LongType LongType LongType LongType DecimalType(38,0) StringType StringType StringType StringType DecimalType(38,0)* DecimalType(38,0) DecimalType(38,0) DecimalType(38,0) DecimalType(38,0) StringType StringType StringType StringType DoubleType DoubleType DoubleType StringType StringType DoubleType StringType StringType StringType DateType DateType StringType StringType StringType StringType DateType TimestampType StringType TimestampType TimestampType StringType StringType StringType StringType TimestampType TimestampType StringType StringType StringType StringType StringType StringType StringType StringType StringType StringType 请注意,对于 DecimalType(38,0)*,上表特意没有涵盖所有其他比例和精度的组合,因为目前我们仅推断像
BigInteger/BigInt这样的十进制类型。例如,1.1 被推断为 double 类型。 -
从 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 在 Hive 2.2 中引入的新行为 (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 无法读取由先前版本创建的视图。在这种情况下,您需要使用较新版本的 Spark,通过
ALTER VIEW AS或CREATE OR REPLACE VIEW AS重新创建视图。
从 Spark SQL 2.0 升级到 2.1
-
数据源表现在将分区元数据存储在 Hive 元存储中。这意味着诸如
ALTER TABLE PARTITION ... SET LOCATION之类的 Hive DDL 现在可用于使用数据源 API 创建的表。-
旧版数据源表可以通过
MSCK REPAIR TABLE命令迁移到此格式。建议迁移旧版表,以便利用 Hive DDL 支持和改进的规划性能。 -
要确定表是否已迁移,请在对表执行
DESCRIBE FORMATTED命令时查找PartitionProvider: Catalog属性。
-
-
对数据源表的
INSERT OVERWRITE TABLE ... PARTITION ...行为进行了更改。-
在以前的 Spark 版本中,即使给定了分区规范,
INSERT OVERWRITE也会覆盖整个数据源表。现在,仅覆盖符合规范的分区。 -
请注意,这仍然与 Hive 表的行为不同,Hive 表的行为是仅覆盖与新插入数据重叠的分区。
-
从 Spark SQL 1.6 升级到 2.0
-
SparkSession现在是 Spark 的新入口点,取代了旧的SQLContext和HiveContext。请注意,保留了旧的 SQLContext 和 HiveContext 以实现向后兼容性。一个新的catalog接口可以从SparkSession中访问 - 现有的数据库和表访问 API(例如listTables,createExternalTable,dropTempView,cacheTable)已移至此处。 -
Dataset API 和 DataFrame API 已统一。在 Scala 中,
DataFrame成为Dataset[Row]的类型别名,而 Java API 用户必须将DataFrame替换为Dataset<Row>。类型化转换(例如map,filter和groupByKey)和非类型化转换(例如select和groupBy)都可在 Dataset 类上使用。由于编译时类型安全在 Python 和 R 中不是语言特性,因此 Dataset 的概念不适用于这些语言的 API。相反,DataFrame仍然是主要的编程抽象,类似于这些语言中单节点数据框的概念。 -
Dataset 和 DataFrame API 中的
unionAll已被弃用,并被union取代。 -
Dataset 和 DataFrame API 中的
explode已被弃用,作为替代,请使用带有select或flatMap的functions.explode()。 -
Dataset 和 DataFrame API 中的
registerTempTable已被弃用,并被createOrReplaceTempView取代。 -
对 Hive 表的
CREATE TABLE ... LOCATION行为进行了更改。-
从 Spark 2.0 开始,
CREATE TABLE ... LOCATION等同于CREATE EXTERNAL TABLE ... LOCATION,以防止意外删除用户提供位置中的现有数据。这意味着,在 Spark SQL 中创建的具有用户指定位置的 Hive 表始终是 Hive 外部表。删除外部表不会删除数据。用户不允许为 Hive 管理的表指定位置。请注意,这与 Hive 的行为不同。 -
因此,对这些表执行
DROP TABLE语句不会删除数据。
-
-
spark.sql.parquet.cacheMetadata不再使用。有关详细信息,请参阅 SPARK-13664。
从 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 与 SUM)。
-
JSON 数据源不会自动加载由其他应用程序创建的新文件(即未通过 Spark SQL 插入到数据集中的文件)。对于 JSON 持久表(即表的元数据存储在 Hive 元存储中),用户可以使用
REFRESH TABLESQL 命令或HiveContext的refreshTable方法将这些新文件包含到表中。对于代表 JSON 数据集的 DataFrame,用户需要重新创建 DataFrame,新的 DataFrame 将包含新文件。
从 Spark SQL 1.3 升级到 1.4
DataFrame 数据读取器/写入器接口
根据用户反馈,我们创建了一个新的、更流畅的 API 用于读取数据 (SQLContext.read) 和写入数据 (DataFrame.write),并弃用了旧的 API(例如 SQLContext.parquetFile, SQLContext.jsonFile)。
有关更多信息,请参阅 SQLContext.read ( Scala, Java, Python ) 和 DataFrame.write ( Scala, Java, Python ) 的 API 文档。
DataFrame.groupBy 保留分组列
根据用户反馈,我们将 DataFrame.groupBy().agg() 的默认行为更改为在生成的 DataFrame 中保留分组列。要保持 1.3 版本中的行为,请将 spark.sql.retainGroupColumns 设置为 false。
import pyspark.sql.functions as func
# In 1.3.x, in order for the grouping column "department" to show up,
# it must be included explicitly as part of the agg function call.
df.groupBy("department").agg(df["department"], func.max("age"), func.sum("expense"))
# In 1.4+, grouping column "department" is included automatically.
df.groupBy("department").agg(func.max("age"), func.sum("expense"))
# Revert to 1.3.x behavior (not retaining grouping column) by:
sqlContext.setConf("spark.sql.retainGroupColumns", "false")// In 1.3.x, in order for the grouping column "department" to show up,
// it must be included explicitly as part of the agg function call.
df.groupBy("department").agg($"department", max("age"), sum("expense"))
// In 1.4+, grouping column "department" is included automatically.
df.groupBy("department").agg(max("age"), sum("expense"))
// Revert to 1.3 behavior (not retaining grouping column) by:
sqlContext.setConf("spark.sql.retainGroupColumns", "false")// In 1.3.x, in order for the grouping column "department" to show up,
// it must be included explicitly as part of the agg function call.
df.groupBy("department").agg(col("department"), max("age"), sum("expense"));
// In 1.4+, grouping column "department" is included automatically.
df.groupBy("department").agg(max("age"), sum("expense"));
// Revert to 1.3 behavior (not retaining grouping column) by:
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。这主要是因为 DataFrames 不再直接继承自 RDD,而是通过其自己的实现提供了 RDD 所提供的大部分功能。DataFrames 仍然可以通过调用 .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 而不是特定于语言的集合)。在某些没有通用类型的情况下(例如,用于传入闭包或映射),则改为使用函数重载。
此外,Java 特定的类型 API 已被删除。Scala 和 Java 的用户都应使用 org.apache.spark.sql.types 中存在的类来以编程方式描述模式。
隐式转换的隔离和 dsl 包的移除(仅限 Scala)
许多 Spark 1.3 之前的代码示例以 import sqlContext._ 开头,这会将 sqlContext 中的所有函数带入作用域。在 Spark 1.3 中,我们将用于将 RDD 转换为 DataFrame 的隐式转换隔离到了 SQLContext 内部的一个对象中。用户现在应该编写 import sqlContext.implicits._。
此外,隐式转换现在仅增强由 Product(即样例类或元组)组成的 RDD,并带有 toDF 方法,而不是自动应用。
当在 DSL(现在已替换为 DataFrame API)中使用函数时,用户过去常常导入 org.apache.spark.sql.catalyst.dsl。相反,应使用公共 dataframe 函数 API:import org.apache.spark.sql.functions._。
移除 org.apache.spark.sql 中 DataType 的类型别名(仅限 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 元存储、SerDes 和 UDF 兼容。目前,Hive SerDes 和 UDF 基于内置的 Hive,Spark SQL 可以连接到不同版本的 Hive 元存储(从 2.0.0 到 2.3.10 以及 3.0.0 到 4.1.0。另请参阅 与不同版本的 Hive 元存储进行交互)。
在现有 Hive 数据仓库中部署
Spark SQL Thrift JDBC 服务器旨在与现有的 Hive 安装“开箱即用”地兼容。您无需修改现有的 Hive 元存储或更改表的放置位置或分区。
支持的 Hive 功能
Spark SQL 支持绝大多数 Hive 功能,例如
- Hive 查询语句,包括
SELECTGROUP BYORDER BYDISTRIBUTE BYCLUSTER BYSORT BY
- 所有 Hive 运算符,包括
- 关系运算符 (
=,<=>,==,<>,<,>,>=,<=等) - 算术运算符 (
+,-,*,/,%等) - 逻辑运算符 (
AND,OR等) - 复杂类型构造函数
- 数学函数 (
sign,ln,cos等) - 字符串函数 (
instr,length,printf等)
- 关系运算符 (
- 用户定义函数 (UDF)
- 用户定义聚合函数 (UDAF)
- 用户定义序列化格式 (SerDes)
- 窗口函数
- 连接 (Joins)
JOIN{LEFT|RIGHT|FULL} OUTER JOINLEFT SEMI JOINLEFT ANTI JOINCROSS JOIN
- 联合 (Unions)
- 子查询
-
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)
-
-
- 采样
- Explain
- 包含动态分区插入的分区表
- 视图 (View)
-
如果在视图定义查询中未指定列别名,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 TABLECREATE TABLE AS SELECTCREATE TABLE LIKEALTER TABLE
- 大多数 Hive 数据类型,包括
TINYINTSMALLINTINTBIGINTBOOLEANFLOATDOUBLESTRINGBINARYTIMESTAMPDATEARRAY<>MAP<>STRUCT<>
不支持的 Hive 功能
以下是我们尚不支持的 Hive 功能列表。这些功能中的大多数在 Hive 部署中很少使用。
深奥的 Hive 功能
UNION类型- 唯一连接 (Unique join)
- 列统计信息收集:Spark SQL 目前不通过扫描来收集列统计信息,仅支持填充 Hive 元存储的 sizeInBytes 字段。
Hive 输入/输出格式
- CLI 的文件格式:对于返回给 CLI 的结果,Spark SQL 仅支持 TextOutputFormat。
- Hadoop 存档
Hive 优化
少数 Hive 优化尚未包含在 Spark 中。其中一些(例如索引)由于 Spark SQL 的内存计算模型而不太重要。其他则计划在 Spark SQL 的未来版本中实现。
- 块级位图索引和虚拟列(用于构建索引)
- 自动确定连接和 groupbys 的 reducer 数量:目前,在 Spark SQL 中,您需要使用 “
SET spark.sql.shuffle.partitions=[num_tasks];” 在 shuffle 后控制并行度。 - 仅元数据查询:对于仅使用元数据即可回答的查询,Spark SQL 仍然会启动任务来计算结果。
- 倾斜数据标志:Spark SQL 不遵循 Hive 中的倾斜数据标志。
- 连接中的
STREAMTABLE提示:Spark SQL 不遵循STREAMTABLE提示。 - 合并多个小文件以获取查询结果:如果结果输出包含多个小文件,Hive 可以选择将小文件合并为较少的几个大文件,以避免溢出 HDFS 元数据。Spark SQL 不支持此功能。
Hive UDF/UDTF/UDAF
并非 Hive UDF/UDTF/UDAF 的所有 API 都受 Spark SQL 支持。以下是不支持的 API:
getRequiredJars和getRequiredFiles(UDF和GenericUDF) 是自动包含该 UDF 所需额外资源的函数。- 尚不支持
GenericUDTF中的initialize(StructObjectInspector)。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 视为秒。