引言
最近帮同事排查了一个诡异的数据库问题,原本以为只是 SQL 写法或者字符集的锅,没想到竟然是 MySQL 官方确认的 Bug(编号 #110104)!
而且这个 Bug 很隐蔽:只有在 使用 UNION
合并查询 + WHERE 条件中包含中文字符 时才会触发。幸运的是,官方给出了明确的临时解决方案,并在 8.0.33 中修复了。
这篇文章分享整个排查过程,希望你也能少走弯路。
问题现象
同事给我发来一段 SQL:
SELECT * FROM (
SELECT id, customer_name FROM order_202401
UNION
SELECT id, customer_name FROM order_202402
) AS tmp
WHERE customer_name LIKE '%张三%';
他说:“明明库里有叫张三的记录,结果查出来是空的,但把张三改成 zhangsan
又能查出来。”
我第一反应是字符集问题,但深入一查,才发现是个更深的坑。
排查过程
我们按以下思路一一排查:
✅ 表的字符集:都是
utf8mb4
✅ 字段类型:
customer_name
是标准varchar
✅
EXPLAIN
执行计划没看出什么异常✅ 不使用 UNION 就能正常查出中文
然后我们尝试将查询改成只查一个表:
SELECT * FROM order_202401 WHERE customer_name LIKE '%张三%';
✅ 可以查出来!
但只要套上 UNION
和子查询,再加上中文 LIKE
条件,就查不到了……
此时我基本可以断定,是查询优化器在处理 UNION + 派生表 + 中文 WHERE 时出了问题。
最终定位:MySQL Bug #110104
我们在 MySQL 官方 Bug 跟踪平台 上搜到了一个非常符合的条目:
Bug #110104 - In 8.0.32, the matching query of Chinese characters is included, and the result set is incorrect.
简而言之:
只要用
UNION
合并多个子查询再用
WHERE
加上中文条件过滤派生表就可能会导致查询失效(结果为空)
官方修复与解决方案
修复版本:MySQL 8.0.33
临时解决方案:关闭
derived_condition_pushdown
优化器
-- 会话级别关闭(推荐)
SET SESSION optimizer_switch='derived_condition_pushdown=off';
-- 或全局关闭(需管理员权限)
SET GLOBAL optimizer_switch='derived_condition_pushdown=off';
一行指令,问题立刻解决!中文能查出来了!
示例对比
-- 失败示例(开启 derived_condition_pushdown 时)
SELECT * FROM (
SELECT id, name FROM user_2023
UNION
SELECT id, name FROM user_2024
) AS u
WHERE name LIKE '%李四%';
-- 返回空结果 ❌
-- 成功示例(关闭 derived_condition_pushdown)
SET SESSION optimizer_switch='derived_condition_pushdown=off';
SELECT * FROM (
SELECT id, name FROM user_2023
UNION
SELECT id, name FROM user_2024
) AS u
WHERE name LIKE '%李四%';
-- 正常返回结果 ✅
什么是 derived_condition_pushdown?
这是 MySQL 优化器的一个选项:
如果开启,MySQL 会尝试将外层
WHERE
条件下推到子查询内部,提高查询效率。
但在某些情况下(尤其涉及中文字符匹配时),这个优化会错误地处理条件,导致本不该过滤掉的数据被排除。
总结
开发建议
如果你使用的是 MySQL 8.0.32 且有复杂 SQL 查询(尤其涉及 UNION、中文 LIKE、子查询等),建议检查是否触发该 Bug。
临时解决方案靠谱,但长期建议尽快升级数据库。
最后
这次帮同事排查的经历让我再次意识到:
不是所有奇怪的行为都是你代码的问题,也可能是数据库自己翻车了。