You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark 3.5.0中MySQL ENUM列WHERE条件UPPER函数失效问题

Spark 3.5.0 升级后 MySQL ENUM 字段 UPPER 过滤失效问题及无代码修改解决方案

问题背景

Spark作业从3.3.1升级至3.5.0后,查询MySQL数据库时,UPPER(col) = UPPER(value)形式的过滤条件失效。涉及的WHERE子句为:

UPPER(vn) = 'ERICSSON' AND (upper(st) = 'OPEN' OR upper(st) = 'REOPEN' OR upper(st) = 'CLOSED')

其中st列是MySQL的ENUM类型,这是问题的核心根源。

物理计划对比

Spark 3.3.1 版本Filter阶段

+- Filter ((upper(vn#11) = ERICSSON) AND (((upper(st#42) = OPEN) OR (upper(st#42) = REOPEN)) OR (upper(st#42) = CLOSED)))

Spark 3.5.0 版本Filter阶段

+- Filter ((upper(vn#11) = ERICSSON) AND (((upper(staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils, StringType, readSidePadding, st#42, 13, true, false, true)) = OPEN) OR (upper(staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils, StringType, readSidePadding, st#42, 13, true, false, true)) = REOPEN)) OR (upper(staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils, StringType, readSidePadding, st#42, 13, true, false, true)) = CLOSED)))

问题根源

Spark 3.4.0新增spark.sql.readSideCharPadding配置项,默认值为true。该配置触发CharVarcharCodegenUtils类的readSidePadding方法,对读取的字符串进行空格填充:

public static UTF8String readSidePadding(UTF8String inputStr, int limit) {
    int numChars = inputStr.numChars();
    if (numChars == limit) {
      return inputStr;
    } else if (numChars < limit) {
      return inputStr.rpad(limit, SPACE);
    } else {
      return inputStr;
    }
  }

该方法错误地对ENUM类型值执行空格填充,导致UPPER(st)处理后的值带空格,无法与目标字符串匹配。

移除UPPER函数后的现象

去掉WHERE条件中的UPPER函数后,Filter阶段物理计划显示填充空格的值能正常匹配,查询返回正确结果:

+- Filter (((staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils,      StringType, readSidePadding, st#42, 13, true, false, true) = OPEN         ) OR (staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils, StringType, readSidePadding, st#42, 13, true, false, true) = REOPEN       )) OR (staticinvoke(class org.apache.spark.sql.catalyst.util.CharVarcharCodegenUtils, StringType, readSidePadding, st#42, 13, true, false, true) = CLOSED       ))

修改spark.sql.readSideCharPadding的测试结果

将spark.sql.readSideCharPadding设为false后,出现两种情况:

  1. 保留UPPER函数:过滤器不下推至MySQL,查询正常运行,物理计划如下:
+- Filter (((upper(st#42) = OPEN) OR (upper(st#42) = REOPEN)) OR (upper(st#42) = CLOSED))
  1. 移除UPPER函数:过滤器下推至MySQL,但条件值带空格,无法获取数据:
PushedFilters: [*IsNotNull(vn), *EqualTo(vn,ERICSSON), *Or(Or(EqualTo(st,OPEN         ),EqualTo(st,REOPEN       )),EqualTo(st,CLOSED       ))]

无代码修改的解决方案

方案1:针对ENUM字段单独禁用填充

Spark 3.5.0支持为特定列设置字符填充规则,可通过JDBC选项或全局配置为st列禁用读侧空格填充:

  • 读取MySQL数据时添加JDBC参数:
.option("spark.sql.readSideCharPadding.st", "false")
  • 或在Spark全局配置中添加:
spark.sql.readSideCharPadding.st=false

此方案既保留其他列的默认填充行为,又避免ENUM字段被错误填充。

方案2:全局禁用填充+关闭过滤器下推

如果不需要过滤器下推至MySQL,可全局设置:

spark.sql.readSideCharPadding=false
spark.sql.pushDownFilters=false

所有过滤器将在Spark端处理,既避免ENUM字段填充空格,也不会出现下推时的空格匹配问题。

方案3:SQL层面添加TRIM(无需修改业务代码,仅调整SQL模板)

若允许通过配置化方式修改SQL,可在UPPER前添加TRIM去除填充空格:

UPPER(vn) = 'ERICSSON' AND (upper(TRIM(st)) = 'OPEN' OR upper(TRIM(st)) = 'REOPEN' OR upper(TRIM(st)) = 'CLOSED')

内容的提问来源于stack exchange,提问作者Suyash Ajmera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:10:31