从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 2.4之前的行为,可以使用带有显式元数据的APIas(alias: String, metadata: Metadata)
。
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
条目的键,并且没有生效,SET
命令也可以正常工作,而不会发出任何警告,因为该命令不会更新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
始终返回Spark DDL,即使给定的表是Hive SerDe表。要生成Hive DDL,请改用SHOW CREATE TABLE 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及以下版本中,如果第二个参数是分数或字符串值,它会被强制转换为整数值,并且结果将是一个日期值,即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
是分数或字符串值,它会被强制转换为整数值,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 3.0中,不能再使用内置函数(如
CreateMap
、MapFromArrays
等)创建具有映射类型键的映射值。用户可以使用map_entries
函数将映射转换为array<struct<key, value>>
来解决此问题。此外,用户仍然可以从数据源或Java/Scala集合中读取具有映射类型键的映射值,尽管不鼓励这样做。 -
在Spark 3.0中,不能再使用内置函数(如
CreateMap
、StringToMap
等)创建具有重复键的映射。如果找到重复键,Spark会抛出RuntimeException
异常。可以将spark.sql.mapKeyDedupPolicy
设置为LAST_WIN
以使用最后一个键策略进行键去重。用户仍然可以从不强制执行键去重的数据源(例如Parquet)中读取具有重复键的映射值,但行为是未定义的。 -
在Spark 3.0中,默认情况下不允许使用
org.apache.spark.sql.functions.udf(AnyRef, DataType)
方法创建未指定返回类型的UDF。建议删除返回类型参数以自动切换到类型化的Scala UDF,或将spark.sql.legacy.allowUntypedScalaUDF
设置为true
以继续使用它。在Spark 2.4及以下版本中,如果org.apache.spark.sql.functions.udf(AnyRef, DataType)
接收到一个带有原始类型参数的Scala闭包,则返回的UDF在输入值为null时返回null。然而,在Spark 3.0中,如果输入值为null,UDF将返回Java类型的默认值。例如,val f = udf((x: Int) => x, IntegerType)
,如果列x
为null,在Spark 2.4及以下版本中,f($"x")
返回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中,执行器端会像其他UDF一样执行没有参数的Java UDF。在Spark 2.4及以下版本中,只有没有参数的Java UDF在Driver端执行,并将结果传播到执行器端,这在某些情况下可能更高效,但也会导致一些正确性问题。
-
java.lang.Math
的log
、log1p
、exp
、expm1
和pow
的结果可能因平台而异。在Spark 3.0中,与java.lang.StrictMath
一致,等价的SQL函数(包括LOG10
等相关SQL函数)的结果与java.lang.Math
一致。在几乎所有情况下,它们的返回值没有任何差异,差异非常小,但在x86平台上可能与java.lang.Math
不完全匹配,例如,log(3.0)
的值在Math.log()
和StrictMath.log()
之间变化。 -
在Spark 3.0中,当将字符串字面量转换为
Double
或Float
类型时,Cast
函数对诸如’Infinity’、‘+Infinity’、‘-Infinity’、‘NaN’、‘Inf’、‘+Inf’、'-Inf’这样的字符串字面量进行不区分大小写处理,以确保与其他数据库系统的兼容性。下表显示了此行为变化的示例:操作 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及以下版本中,将字符串转换为整数和布尔值时,不会去除两端的空白字符;对于日期时间类型,只会去除尾随的空格(ASCII 32)。
Spark版本3.0中的查询引擎变化
-
在Spark 3.0及以下版本中,不再支持类似
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 3.0之前的版本中,它们被解析为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 3.0之前的版本中,不考虑from
边界,只使用to
边界来截断结果的区间。例如,上述示例中的日时区间字符串会转换为interval 10 hours 20 minutes
。要恢复到Spark 3.0之前的行为,可以将spark.sql.legacy.fromDayTimeString.enabled
设置为true
。 -
在Spark 3.0中,默认情况下不允许负数精度的Decimal类型。例如,类似
1E10BD
的字面量的数据类型为DecimalType(11, 0)
。而在Spark 3.0之前的版本中,数据类型为DecimalType(2, -9)
。要恢复到Spark 3.0之前的行为,可以将spark.sql.legacy.allowNegativeScaleOfDecimal
设置为true
。 -
在Spark 3.0中,一元算术运算符加号(
+
)只接受字符串、数值和时间区间类型的值作为输入。此外,带有整数字符串表示的+
会被强制转换为Double值,例如+'1'
返回1.0
。而在Spark 3.0之前的版本中,该运算符会被忽略,对于它没有类型检查,因此带有+
前缀的所有类型值都是有效的,例如+ array(1, 2)
是有效的,并且结果为[1, 2]
。此外,对于它根本没有类型强制转换,例如,在Spark 2.4中,+'1'
的结果是字符串1
。 -
在Spark 3.0中,如果数据集查询包含由自身连接引起的列引用歧义,查询将失败。一个典型的例子是:
val df1 = ...; val df2 = df1.filter(...);
,然后df1.join(df2, df1("a") > df2("a"))
返回一个空结果,这很令人困惑。这是因为Spark无法解析指向自身连接的数据集列引用,并且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定义优先于外部定义。例如,将该配置设置为CORRECTED
,WITH t AS (SELECT 1), t2 AS (WITH t AS (SELECT 2) SELECT * FROM t) SELECT * FROM t2
返回2
,而将其设置为LEGACY
时,结果为1
,这是Spark 2.4及以下版本的行为。 -
在Spark 3.0中,
spark.sql.crossJoin.enabled
配置项变为内部配置项,默认值为true
,因此默认情况下Spark不会对隐式交叉连接的SQL引发异常。 -
在Spark 2.4及以下版本中,浮点数/双精度数-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在处理from_utc_timestamp等函数时会被默默地忽略并替换为GMT时区。在Spark 3.0中,将拒绝这些无效的时区ID,并抛出
java.time.DateTimeException
异常。 -
在Spark 3.0中,解析、格式化和转换日期、时间戳以及提取年、日等子组件时使用Proleptic Gregorian日历。Spark 3.0使用基于ISO年表的
java.time
包中的Java 8 API类进行这些操作。而在Spark 2.4及以下版本中,这些操作是使用混合日历(朱利安+格里高利)(Julian + Gregorian)进行的。这些变化影响了Spark 3.0的以下API:-
时间戳/日期字符串的解析/格式化。这会影响CSV/JSON数据源以及使用用户指定模式进行解析和格式化的
unix_timestamp
、date_format
、to_unix_timestamp
、from_unixtime
、to_date
、to_timestamp
函数。在Spark 3.0中,我们在sql-ref-datetime-pattern.md
中定义了自己的模式字符串,它是通过底层的java.time.format.DateTimeFormatter
实现的。新的实现对其输入进行严格检查。例如,如果模式为yyyy-MM-dd
,则无法解析2015-07-22 10:00:00
时间戳,因为解析器没有消耗整个输入。另一个例子是,使用dd/MM/yyyy hh:mm
模式无法解析31/01/2015 00:00
输入,因为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值相同。该转换基于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
。可以通过将spark.sql.legacy.typeCoercion.datetimeToString.enabled
设置为true
来恢复先前将Date/Timestamp
转换为String
的行为。 -
在Spark 3.0中,支持特殊值在从字符串到日期和时间戳的转换中。这些特殊值只是一种简写的标记,当读取时被转换为普通的日期或时间戳值。对于日期,支持以下字符串值:
epoch [zoneId]
- 1970-01-01today [zoneId]
- 当前日期(根据spark.sql.session.timeZone
指定的时区)yesterday [zoneId]
- 当前日期减1天tomorrow [zoneId]
- 当前日期加1天now
- 运行当前查询的日期。与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)
值,精确到微秒。例如,extract(second from to_timestamp('2019-09-20 10:10:10.1'))
的结果是10.100000
。而在Spark 2.4及更早版本中,它返回一个IntegerType
值,对于前面的示例,结果为10
。
数据源变化
-
在Spark 3.0及以下版本中,当使用Spark本机数据源(parquet/orc)读取Hive SerDe表时,Spark不再推断实际文件模式并更新元数据存储中的表模式。这对终端用户不应该造成任何问题,但如果确实存在问题,可以将
spark.sql.hive.caseSensitiveInferenceMode
设置为INFER_AND_SAVE
。 -
在Spark 3.0中,如果分区列的值无法转换为用户提供的模式,则将分区列的值视为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及以下版本中,JSON数据源和
from_json
等JSON函数在StructType
模式下将错误的JSON记录转换为所有字段均为null的行(在PERMISSIVE模式下)。在Spark 3.0中,如果某些JSON列值被成功解析和转换为所需类型,则返回的行可以包含非null字段。 -
在Spark 3.0中,JSON数据源和
schema_of_json
函数从字符串值推断TimestampType类型,如果它们与JSON选项timestampFormat
定义的模式匹配。将JSON选项inferTimestamp
设置为false
以禁用此类类型推断。 -
在Spark 2.4及以下版本中,CSV数据源在PERMISSIVE模式下将格式错误的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中,通过
cloneSession()
创建Spark会话时,新创建的Spark会话继承其父SparkContext
的配置,即使相同的配置在父Spark会话中具有不同的值。在Spark 3.0中,父SparkSession
的配置优先于父SparkContext
。可以通过将spark.sql.legacy.sessionInitWithConfigDefaults
设置为true
来恢复旧的行为。 -
在Spark 3.0中,如果在
Spark SQL配置
中找不到hive.default.fileformat
,则会回退到SparkContext
的Hadoop配置
中存在的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元存储的版本设置
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。 -
当使用SQL进行脚本转换时,Hive 1.2和Hive 2.3之间的字符串表示可能不同,具体取决于hive的行为。在Hive 1.2中,字符串表示省略尾随零。但在Hive 2.3中,如果需要,它总是填充为18位数字,并包含尾随零。
-