再遇 MySQL “一招必杀” Bug

再遇 MySQL “一招必杀” Bug

ShawnYan Lv.6

TL;DR

该 Bug 与 Derived Condition Pushdown Optimization 相关,从 MySQL 8.0.26 引入,8.0.27 和 8.0.28 仍受影响,直到 MySQL 8.0.29 被修复。
但是,MySQL 8.0.29 有其他致命缺陷,官网已经移除下载链接,建议升级到新版本 MySQL 8.0.33 或 MySQL 8.0.34。

1.png 2.png

问题现象

该问题是 vx 群里的好友发现的,在 MySQL 5.7、TiDB 中并未发现该问题,但当应用程序运行在 MySQL 8.0.27 时,问题出现,最终定位到具体 SQL,并提炼出最小化复现步骤,诱发问题的 SQL 如下。

1
2
3
4
5
6
7
8
9
10
11
12
SELECT 1
FROM (
SELECT bb.c_int+1 AS c_int111
FROM (
SELECT aa.c_int AS c_int
FROM (
SELECT 0 AS c_int
) aa
) bb
) cc
WHERE c_int111 > 1
;

经过多版本实验比对,发现该 SQL 在较低版本(8.0.25 及以下)或较高版本(8.0.29 及以上)的 MySQL 中执行正常,在兼容 MySQL 5.7 协议的 TiDB 中执行正常,在 MariaDB 中也执行正常。

3.png

但是在 MySQL 8.0.27 / 8.0.28 中,该 SQL 执行异常。

4.png

But again, 在 MySQL 8.0.26 中,该 SQL 将导致 DB Crash,“一招致命”。

5.png

到此,可以确定,这是 MySQL 8.0.26 的一个 Bug,并对 MySQL 8.0.27 和 8.0.28 持续产生影响,直到 MySQL 8.0.29 被完整修复,恢复可用态。

Bug 确认

知道了 Bug 影响和被修复的版本,以及 crash log 就降低了寻找 Bug 号的难度。
对于 MySQL 社区版,除了搜索引擎,通常有三个途径可以用来找寻 Bug 号,分别是 MySQL 官网的 Release Notes,MySQL Bug 系统,以及 MySQL 社区版源码。

对于此案例,可以通过错误日志来快速定位,摘取重要日志如下。

1
2
3
4
/usr/sbin/mysqld(Condition_pushdown::replace_columns_in_cond(Item**, bool)+0x74) [0x55715b87c6e4]
/usr/sbin/mysqld(Condition_pushdown::make_cond_for_derived()+0x2a3) [0x55715b87d7f3]
/usr/sbin/mysqld(Query_block::push_conditions_to_derived_tables(THD*)+0xda) [0x558385bda21a]
/usr/sbin/mysqld(Query_block::prepare(THD*, mem_root_deque<Item*>*)+0xd7c) [0x558385bef7fc]

简单解释上面四个方法的含义:

  1. Condition_pushdown::replace_columns_in_cond:
    用派生表表达式替换将推入此派生表的条件中的列。
  2. Condition_pushdown::make_cond_for_derived:创建一个可以下推到派生表的条件,并将其下推。
  3. Query_block::push_conditions_to_derived_tables:将此查询块的WHERE条件的部分推送到物化派生表。
  4. Query_block::prepare:准备查询块进行优化。解析表和列信息。

在公开的 Bug 系统中找到了一条记录 (Bug #104574),S2 严重级别。

6.png

与之对应的 commit 记录为:518c2ae85b55f78565908567a02de55d0a8a477a

1
2
3
Bug#33197276: regression: Assertion `cur_query_block != select' failed.
and segfault in Condition_pushdown::replace_columns_in_cond
Bug#33209907: condition seems not correct

继续排查 sql_derived.cc 这个文件在 8.0.29 发版之前的提交信息,找到可能与之相关的 commit 记录。

顺藤摸瓜,找到了 MySQL 8.0.29 修复的那条 commit 记录 (3ca1c67dfdbc9e2972810773474fbfe70fdabaa8)。

1
2
WL#13730 - Condition pushdown to materialized derived tables
with set operations

回头再看 Release Notes,可以找到如下记录:

MySQL 8.0.27
In certain cases, the view reference cloned when pushing a condition down to a derived table was not always resolved in the desired context. In addition, a check for a null condition was not performed correctly. (Bug #104574, Bug #33209907, Bug #33197276)

MySQL 8.0.29
The derived materialized table condition pushdown optimization can now be used with most unions. This means that an outer WHERE condition can now be pushed down to every query block of the query expression of the materialized derived table or view.

This can now be done for most UNION queries. For exceptions, and additional information, see Derived Condition Pushdown Optimization. (Bug #24012, Bug #36802, Bug #106006, Bug #11746156, Bug #11748590, Bug #13650627, Bug #30587347, Bug #33318096, Bug #33738597, WL #13730)

到此,可以初步判定,这个 Bug 是由 MySQL 8.0.22 新引入的变量 derived_condition_pushdown 及其相关代码逻辑处理不恰引起的。

解决办法

找到原因,解决起来就很简单了。

  1. (workaround) 参数 derived_condition_pushdown 默认值为 ON,修改全局变量将其改为 OFF,并将其进行持久化设定。
1
2
SET GLOBAL optimizer_switch='...,derived_condition_pushdown=off';
SET PERSIST optimizer_switch='...,derived_condition_pushdown=off';
7.png
  1. 跳过问题版本,升级到更高版本,如 MySQL 8.0.33 或 MySQL 8.0.34。

Ps.

在查阅资料过程中,发现在新版本还有关于 derived_condition_pushdown 的 Bug,比如 Bug#109699 , Bug#110228 影响到 MySQL 8.0.32,在 MySQL 8.0.33 得到修复。看来这还是个有故事的 feature。

总结

在修复一个 Bug 时,可能引入新的 Bug,所以功能测试、版本适配很重要。
DBA 需要有不同版本、不同架构,甚至不同类型数据库的测试环境,用来测试或验证各种概念、问题。
没想到一个新特性影响了那么多版本,希望 MySQL 8.1 之后会带来不一样的感受~

logo.jpg
  • Title: 再遇 MySQL “一招必杀” Bug
  • Author: ShawnYan
  • Created at: 2023-07-25 16:46:36
  • Updated at: 2023-07-25 16:46:36
  • Link: https://shawnyan.cn/2023/mysql/mysql-8.0.26-bug-crash/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments