【Spark床头书系列】从Spark SQL 2.4升级到3.0哪些变化

本文详细介绍了从Spark SQL 2.4升级到3.0的多方面变化,包括Dataset/DataFrame API、DDL语句、UDF和内置函数、查询引擎、数据源等。如在API方面,分组属性命名改变;DDL语句中类型转换遵循ANSI标准等,还给出了恢复旧行为的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从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.nameColumn.as 中传播。在Spark 2.4及更早版本中,NamedExpression 的元数据在调用API时作为新列的explicitMetadata设置,即使底层的NamedExpression 更改了元数据,也不会更改。要恢复Spark 2.4之前的行为,可以使用带有显式元数据的API as(alias: String, metadata: Metadata)

DDL语句

  • 在Spark 3.0中,当向表列插入不同数据类型的值时,将根据ANSI SQL标准执行类型转换。某些不合理的类型转换,如将string转换为intdouble转换为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 DBPROPERTIESALTER 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_adddate_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函数支持两种模式:PERMISSIVEFAILFAST。可以通过mode选项设置模式,默认模式变为PERMISSIVE。在之前的版本中,from_json的行为既不符合PERMISSIVE也不符合FAILFAST,特别是在处理格式错误的JSON记录时。例如,使用模式a INT解析JSON字符串{"a" 1},在之前的版本中被转换为null,而在Spark 3.0中转换为Row(null)

  • 在Spark 3.0中,不能再使用内置函数(如CreateMapMapFromArrays等)创建具有映射类型键的映射值。用户可以使用map_entries函数将映射转换为array<struct<key, value>>来解决此问题。此外,用户仍然可以从数据源或Java/Scala集合中读取具有映射类型键的映射值,尽管不鼓励这样做。

  • 在Spark 3.0中,不能再使用内置函数(如CreateMapStringToMap等)创建具有重复键的映射。如果找到重复键,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.Mathloglog1pexpexpm1pow的结果可能因平台而异。在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中,当将字符串字面量转换为DoubleFloat类型时,Cast函数对诸如’Infinity’、‘+Infinity’、‘-Infinity’、‘NaN’、‘Inf’、‘+Inf’、'-Inf’这样的字符串字面量进行不区分大小写处理,以确保与其他数据库系统的兼容性。下表显示了此行为变化的示例:

    操作Spark 3.0之前的结果Spark 3.0的结果
    CAST(‘infinity’ AS DOUBLE)NULLDouble.PositiveInfinity
    CAST(‘+infinity’ AS DOUBLE)NULLDouble.PositiveInfinity
    CAST(‘inf’ AS DOUBLE)NULLDouble.PositiveInfinity
    CAST(‘inf’ AS DOUBLE)NULLDouble.PositiveInfinity
    CAST(‘-infinity’ AS DOUBLE)NULLDouble.NegativeInfinity
    CAST(‘-inf’ AS DOUBLE)NULLDouble.NegativeInfinity
    CAST(‘infinity’ AS FLOAT)NULLFloat.PositiveInfinity
    CAST(‘+infinity’ AS FLOAT)NULLFloat.PositiveInfinity
    CAST(‘inf’ AS FLOAT)NULLFloat.PositiveInfinity
    CAST(‘+inf’ AS FLOAT)NULLFloat.PositiveInfinity
    CAST(‘-infinity’ AS FLOAT)NULLFloat.NegativeInfinity
    CAST(‘-inf’ AS FLOAT)NULLFloat.NegativeInfinity
    CAST(‘nan’ AS DOUBLE)NULLDouble.Nan
    CAST(‘nan’ AS FLOAT)NULLFloat.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)的结果是1cast(' 1\t' as boolean)的结果是truecast('2019-10-10\t as date)的结果是日期值2019-10-10`。在Spark 2.4及以下版本中,将字符串转换为整数和布尔值时,不会去除两端的空白字符;对于日期时间类型,只会去除尾随的空格(ASCII 32)。

Spark版本3.0中的查询引擎变化

  1. 在Spark 3.0及以下版本中,不再支持类似FROM <table>FROM <table> UNION ALL FROM <table>这样的SQL查询。在Hive风格的FROM <table> SELECT <expr>中,SELECT子句是不可忽略的。Hive和Presto都不支持这种语法。这些查询在Spark 3.0中被视为无效。

  2. 在Spark 3.0中,间隔字面量语法不再允许多个from-to单位。例如,SELECT INTERVAL '1-1' YEAR TO MONTH '2-2' YEAR TO MONTH'会抛出解析异常。

  3. 在Spark 3.0中,科学计数法表示的数字(例如1E2)将被解析为Double类型。而在Spark 3.0之前的版本中,它们被解析为Decimal类型。要恢复到Spark 3.0之前的行为,可以将spark.sql.legacy.exponentLiteralAsDecimal.enabled设置为true

  4. 在Spark 3.0中,日时区间字符串将根据fromto边界转换为区间值。如果输入字符串与指定边界定义的模式不匹配,则会抛出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

  5. 在Spark 3.0中,默认情况下不允许负数精度的Decimal类型。例如,类似1E10BD的字面量的数据类型为DecimalType(11, 0)。而在Spark 3.0之前的版本中,数据类型为DecimalType(2, -9)。要恢复到Spark 3.0之前的行为,可以将spark.sql.legacy.allowNegativeScaleOfDecimal设置为true

  6. 在Spark 3.0中,一元算术运算符加号(+)只接受字符串、数值和时间区间类型的值作为输入。此外,带有整数字符串表示的+会被强制转换为Double值,例如+'1'返回1.0。而在Spark 3.0之前的版本中,该运算符会被忽略,对于它没有类型检查,因此带有+前缀的所有类型值都是有效的,例如+ array(1, 2)是有效的,并且结果为[1, 2]。此外,对于它根本没有类型强制转换,例如,在Spark 2.4中,+'1'的结果是字符串1

  7. 在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

  8. 在Spark 3.0中,引入了spark.sql.legacy.ctePrecedencePolicy配置项,用于控制嵌套WITH子句中名称冲突的行为。默认值为EXCEPTION,Spark会抛出AnalysisException异常,强制用户选择他们想要的特定替换顺序。如果设置为CORRECTED(推荐),内部CTE定义优先于外部定义。例如,将该配置设置为CORRECTEDWITH t AS (SELECT 1), t2 AS (WITH t AS (SELECT 2) SELECT * FROM t) SELECT * FROM t2返回2,而将其设置为LEGACY时,结果为1,这是Spark 2.4及以下版本的行为。

  9. 在Spark 3.0中,spark.sql.crossJoin.enabled配置项变为内部配置项,默认值为true,因此默认情况下Spark不会对隐式交叉连接的SQL引发异常。

  10. 在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)]

  11. 在Spark 2.4及以下版本中,无效的时区ID在处理from_utc_timestamp等函数时会被默默地忽略并替换为GMT时区。在Spark 3.0中,将拒绝这些无效的时区ID,并抛出java.time.DateTimeException异常。

  12. 在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_timestampdate_formatto_unix_timestampfrom_unixtimeto_dateto_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来恢复旧的行为。

    • weekofyearweekdaydayofweekdate_truncfrom_utc_timestampto_utc_timestampunix_timestamp函数使用java.time API来计算一年中的周数、一周中的天数,以及在UTC时区之间进行TimestampType值的转换。

    • JDBC选项lowerBoundupperBound与将字符串转换为TimestampType/DateType值相同。该转换基于Proleptic Gregorian日历,并由SQL配置spark.sql.session.timeZone定义的时区确定。而在Spark 2.4及以下版本中,该转换基于混合日历(朱利安+格里高利)和默认系统时区。

    • 格式化TIMESTAMPDATE字面量。

    • 从字符串创建带有类型的TIMESTAMPDATE字面量。在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系统时区。默认时区的不同来源可能会改变带有类型的TIMESTAMPDATE字面量的行为。

  13. 在Spark 3.0中,TIMESTAMP字面量转换为字符串时使用SQL配置spark.sql.session.timeZone。而在Spark 2.4及以下版本中,转换使用Java虚拟机的默认时区。

  14. 在Spark 3.0中,Spark将String在与日期/时间戳进行二进制比较时转换为Date/Timestamp。可以通过将spark.sql.legacy.typeCoercion.datetimeToString.enabled设置为true来恢复先前将Date/Timestamp转换为String的行为。

  15. 在Spark 3.0中,支持特殊值在从字符串到日期和时间戳的转换中。这些特殊值只是一种简写的标记,当读取时被转换为普通的日期或时间戳值。对于日期,支持以下字符串值:

    • epoch [zoneId] - 1970-01-01
    • today [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';

  16. 自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

数据源变化

  1. 在Spark 3.0及以下版本中,当使用Spark本机数据源(parquet/orc)读取Hive SerDe表时,Spark不再推断实际文件模式并更新元数据存储中的表模式。这对终端用户不应该造成任何问题,但如果确实存在问题,可以将spark.sql.hive.caseSensitiveInferenceMode设置为INFER_AND_SAVE

  2. 在Spark 3.0中,如果分区列的值无法转换为用户提供的模式,则将分区列的值视为null。在3.0中,将使用用户提供的模式对分区列的值进行验证。如果验证失败,则抛出异常。可以通过将spark.sql.sources.validatePartitionColumns设置为false来禁用此类验证。

  3. 在Spark 3.0中,如果在递归目录列表期间(即,在中间列表中出现,但在后续阶段无法读取或列出的情况下,由于并发文件删除或对象存储一致性问题)文件或子目录消失,则列表将失败并抛出异常,除非spark.sql.files.ignoreMissingFiles设置为true(默认为false)。在之前的版本中,这些缺失的文件或子目录将被忽略。请注意,此更改的行为仅适用于初始表文件列表(或REFRESH TABLE期间),而不是在查询执行期间:净变化是现在在表文件列表/查询计划期间遵守spark.sql.files.ignoreMissingFiles,而不仅仅在查询执行时。

  4. 在Spark 2.4及以下版本中,JSON数据源的解析器将空字符串视为某些数据类型(如IntegerType)的null值。对于FloatTypeDoubleTypeDateTypeTimestampType,它会在遇到空字符串时失败并抛出异常。Spark 3.0不允许空字符串,并且除了StringTypeBinaryType之外的数据类型都会抛出异常。可以将spark.sql.legacy.json.allowEmptyString.enabled设置为true来恢复先前允许空字符串的行为。

  5. 在Spark 2.4及以下版本中,JSON数据源和from_json等JSON函数在StructType模式下将错误的JSON记录转换为所有字段均为null的行(在PERMISSIVE模式下)。在Spark 3.0中,如果某些JSON列值被成功解析和转换为所需类型,则返回的行可以包含非null字段。

  6. 在Spark 3.0中,JSON数据源和schema_of_json函数从字符串值推断TimestampType类型,如果它们与JSON选项timestampFormat定义的模式匹配。将JSON选项inferTimestamp设置为false以禁用此类类型推断。

  7. 在Spark 2.4及以下版本中,CSV数据源在PERMISSIVE模式下将格式错误的CSV字符串转换为所有字段均为null的行。在Spark 3.0中,如果某些CSV列值被成功解析和转换为所需类型,则返回的行可以包含非null字段。

  8. 在Spark 3.0中,使用用户提供的模式写入Avro文件时,字段将根据Catalyst模式和Avro模式之间的字段名进行匹配,而不是位置。

  9. 在Spark 3.0中,使用用户提供的非空模式写入Avro文件时,即使Catalyst模式可为空,Spark仍然能够写入文件。但是,如果任何记录包含null,则Spark会抛出运行时NullPointerException。

其他变化

  1. 在Spark 2.4中,通过cloneSession()创建Spark会话时,新创建的Spark会话继承其父SparkContext的配置,即使相同的配置在父Spark会话中具有不同的值。在Spark 3.0中,父SparkSession的配置优先于父SparkContext。可以通过将spark.sql.legacy.sessionInitWithConfigDefaults设置为true来恢复旧的行为。

  2. 在Spark 3.0中,如果在Spark SQL配置中找不到hive.default.fileformat,则会回退到SparkContextHadoop配置中存在的hive-site.xml文件。

  3. 在Spark 3.0中,我们对spark-sql接口中的十进制数进行尾部零填充,以与列的精度保持一致,例如:

    查询Spark 2.4Spark 3.0
    SELECT CAST(1 AS decimal(38, 18));11.000000000000000000
  4. 在Spark 3.0中,我们将内置Hive从1.2升级到2.3,这带来了以下影响:

    • 您可能需要根据要连接的Hive元存储的版本设置spark.sql.hive.metastore.versionspark.sql.hive.metastore.jars。例如:如果您的Hive元存储版本是1.2.1,则将spark.sql.hive.metastore.version设置为1.2.1spark.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位数字,并包含尾随零。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值