臭名昭著的、存在20年的MySQL Bug #11472终于被修复了17认证网

正规官方授权
更专业・更权威

臭名昭著的、存在20年的MySQL Bug #11472终于被修复了

最近,小编发现Reddit上有一篇名为《臭名昭著的、存在了20年的MySQL Bug#11472已被修复》的帖子火了。这个从2005年就被提交、伴随MySQL二十年的经典Bug,终于在2026年被修复。

一、 Bug #11472 到底是什么问题?

以下是该Bug的提报者Omer Barnir在2005年提交Bug时的原始描述:

[2005年6月21日 0:08] Omer Barnir

描述:

当由于表中存在外键定义而间接更新/删除表中的行时,该表上的触发器将不会按要求执行,如下所示:

omer@linux:~/source/src50_0620/client> ./mysql --socket=/home/omer/source/src50_0620/mysql-test/var/tmp/master.sock --user=root

欢迎使用 MySQL 监视器。命令以 ; 或 \g 结尾。

你的 MySQL 连接 ID 为 1,服务器版本为:5.0.8-beta-log。

输入 ‘help;’ 或 ‘\h’ 获取帮助。输入 ‘\c’ 清除缓冲区。

mysql > USE test;数据库已更改mysql> mysql> DROP TABLE IF EXISTS t1,t2;查询成功,影响 0 行,2 个警告 (0.00 秒) mysql> mysql> CREATE TABLE t1 (id INT NOT NULL, col1 char(50), PRIMARY KEY (id)) ENGINE=INNODB;查询成功,影响 0 行 (0.01 秒) mysql> CREATE TABLE t2 (id INT PRIMARY KEY, f_id INT, INDEX par_ind (f_id), col1 char(50),     -> FOREIGN KEY (f_id) REFERENCES t1(id) ON DELETE SET NULL) ENGINE=INNODB;查询成功,影响 0 行 (0.01 秒) mysql> mysql> create trigger tr_t2 after update on t2     -> for each row set @counter=@counter+1;查询成功,影响 0 行 (0.00 秒) mysql> mysql> insert into t1 values (1,'Department A');查询成功,影响 1 行 (0.00 秒) mysql> insert into t1 values (2,'Department B');查询成功,影响 1 行 (0.00 秒) mysql> insert into t1 values (3,'Department C');查询成功,影响 1 行 (0.00 秒) mysql> insert into t2 values (1,2,'Emp 1');查询成功,影响 1 行 (0.00 秒) mysql> insert into t2 values (2,2,'Emp 2');查询成功,影响 1 行 (0.00 秒) mysql> insert into t2 values (3,2,'Emp 3');查询成功,影响 1 行 (0.00 秒) mysql> insert into t2 values (4,2,'Emp 4');查询成功,影响 1 行 (0.00 秒) mysql> insert into t2 values (5,2,'Emp 5');查询成功,影响 1 行 (0.00 秒) mysql> set @counter=0;查询成功,影响 0 行 (0.00 秒) mysql> select * from t1; +----+--------------+ | id | col1 | +----+--------------+ | 1 | Department A | | 2 | Department B | | 3 | Department C | +----+--------------+ 3 rows in set (0.00 秒) mysql> select * from t2; +----+------+-------+ | id | f_id | col1 | +----+------+-------+ | 1 | 2 | Emp 1 | | 2 | 2 | Emp 2 | | 3 | 2 | Emp 3 | | 4 | 2 | Emp 4 | | 5 | 2 | Emp 5 |+----+------+-------+ 5 行结果 (0.00 秒) mysql> select @counter+----------+ | @counter | +----------+ | 0 | +----------+ 1 行结果 (0.00 秒) mysql> delete from t1 where id=2;查询成功,影响 1 行 (0.05 秒) mysql> mysql> select * from t1; +----+--------------+ | id | col1 | +----+--------------+ | 1 | Department A | | 3 | Department C | +----+--------------+ 2 行结果 (0.00 秒) mysql> select * from t2; +----+------+------- + | id | f_id | col1 | +----+------+-------+ | 1 | NULL | Emp 1 | | 2 | NULL | Emp 2 | | 3 | NULL | Emp 3 | | 4 | NULL | Emp 4 | | 5 | NULL | Emp 5 | +----+------+-------+ 5 rows in set (0.00 sec) mysql> select @counter+----------+ | @counter | +----------+ | 0 | +----------+ 1 行记录 (0.00 秒) ***** 注意:此时表 t2 中更新了 5 行,                  @count 的值预期为 '5'(每次触发触发器                  都会将其值加 1),但该值仍然为零,表明                  触发器未执行。以下语句显示,当                  直接更新表 't2'                  时,触发器本身会被执行: mysql> update t2 set col1='Emp 5a' where id=5;查询成功,影响 1 行 (0.00 秒)匹配行数:1 更改行数:1 警告数:0 mysql> select * from t2; +----+------+--------+ | id | f_id | col1 | +----+------+--------+ | 1 | NULL | Emp 1 | | 2 | NULL | Emp 2 | | 3 | NULL | Emp 3 | | 4 | NULL | Emp 4 | | 5 | NULL | Emp 5| +----+------+--------+ 5 行结果 (0.00 秒) mysql> select @counter+----------+ | @counter | +----------+ | 1 | +----------+ 1 行结果 (0.00 秒) ***** 在这种情况下,触发器已执行(@count 设置为 '1'mysql> mysql> drop table t2,t1;查询成功,0 行受影响 (0.00 秒) mysql> quit Bye omer@linux:~/source/src50_0620/client>     

如何重现:

在 mysql 客户端中运行以下命令:

USE test; DROP TABLE IF EXISTS t1,t2;CREATE TABLE t1 (id INT NOT NULL, col1 char(50), PRIMARY KEY (id)) ENGINE=INNODB; CREATE TABLE t2 (id INT PRIMARY KEY, f_id INT, INDEX par_ind (f_id), col1 char(50),         FOREIGN KEY (f_id) REFERENCES t1(id) ON DELETE SET NULL) ENGINE=INNODB; CREATE Trigger tr_t2 After Update on t2         For Each Row Set @counter=@counter+1INSERT INTO t1 VALUES (1,'Department A'); INSERT INTO t1 VALUES (2,'Department B'); INSERT INTO t1 VALUES (3,'Department C'); INSERT INTO t2 VALUES (1,2,'Emp 1'); INSERT INTO t2 VALUES (2,2,'Emp 2'); INSERT INTO t2 VALUES (3,2,'Emp 3'); INSERT INTO t2 VALUES (4,2,'Emp 4'); INSERT INTO t2 VALUES (5,2,'Emp 5'); SET @counter=0SELECT * FROM t1; SELECT * FROM t2; SELECT @counterDELETE FROM t1 WHERE id=2SELECT * FROM t1; SELECT * FROM t2; SELECT @counterUPDATE t2 SET col1='Emp 5a' WHERE id=5SELECT * FROM t2; SELECT @counterDROP TABLE t2,t1;

建议的修复方案:

在上述场景中执行触发器。

二、为什么这个Bug能存在二十年?

其实在Omer Barnir提交Bug的当天,MySQL InnoDB存储引擎的创始人Heikki Tuuri就亲自回复:

Omer,这是一个已知问题。如果级联外键子句导致行数据被修改,需要由Dmitri、PEM或其他相关人员提供一种触发机制,让触发器能够正常执行。我会记录此缺陷,直到问题解决。

第二天,他又补充表示,会在MySQL 5.1版本修复该问题,并将该Bug的严重级别降低至P3。

然而事情并未如期推进:

2007年,Heikki Tuuri 改口称MySQL自身的外键实现最终会解决这个问题,但仍然需要一些时间。

2009年,MySQL核心贡献者Konstantin Osipov正式确认 5.1 版本不会修复这个Bug。

2013年,MySQL核心开发团队的资深工程师Ståle Deraas进一步解释,解决这个问题需要大量的开发工作,并非简单的漏洞修复。

面对年复一年的拖延,开发者的不满与质疑也越来越强烈:

[2011 年 3 月 13 日 14:37] 五年过去了,这个漏洞仍然没有修复。请问原因是什么?目标版本是什么?
[2011年8月20日 11:32] 这意味着MySQL InnoDB在过去 5 年里一直不符合 ACID 标准。
[2012年6月21日 2:44] 请问MySQL团队是否有人可以告知我们是否有计划或打算修复这个漏洞?这严重威胁到ACID/完整性……有官方人员愿意对此发表评论吗?
[2012年9月26日 7:49] 七年多了……真是个笑话!我现在已经迁移到 PostgreSQL 了,级联删除触发器也正常工作了,而且速度也更快了 😉
[2013年9月19日 19:24] 这个问题已经报告了8年,显然很多人都受到了影响。而且,目前还没有好的解决方法。这个漏洞什么时候才能修复?
[2014年4月5日 7:16] 这个问题解决了吗?我仍然遇到这个问题。
[2015 年 2 月 28 日 15:30] 距离最初提交错误报告已经过去近十年了,这个问题仍然存在。这些年来,我不得不多次建议用户使用其他关系型数据库管理系统(例如PostgreSQL),因为它们支持这类基本功能。这真是令人失望……

很多人不解,一个早已被官方确认的功能问题,为什么会被搁置如此之久?

2020年,分布式数据库TiDB幕后公司PingCAP的技术支持工程师Daniël van Eeden的留言或许能解释这个问题:

如果这个缺陷在下一个小版本更新中就被修复,那么原本不会执行的触发器会突然生效。尽管依赖这种行为是非常糟糕的设计,但用户仍有可能在无意间陷入这种场景。如果用户在使用基于语句的复制(同样不推荐,基于行的复制优势更明显),这些触发器就可能只在从库而不在主库执行(因为主从小版本不一致),从而导致数据漂移。

要解决这个问题,可行的方式是新增一个系统配置项,允许在现有行为和新修复行为之间切换。

另一种“方案”是为触发器增加一个标记位来控制这一行为,但这很可能不符合ISO SQL标准,而且还需要用户修改所有已存在的触发器。

即便限制基于语句的复制,也只能缓解部分问题,无法彻底根治。

三、一个Bug二十年的社区狂欢

在等待这个Bug修复的过程当中,评论区比Bug本身还精彩:

有给这个Bug庆祝生日的:

[2015年6月21日 8:38] 周年快乐!已经10年了……
[2018年6月21日 18:02] 11472,13岁生日快乐!他们长得真快啊 :’)
[2022 年 6 月 21 日 4:39] 今天是你17岁生日,祝愿你在即将到来的成年生活中好运、幸福、成功!
[2023年2月7日 19:01] 或许我们应该为这个漏洞的18岁生日做点特别的事情?
[2024年12月28日 19:41] 亲爱的Bug,今天是2024年12月29日,你依然在困扰着那些选择使用触发器的少数玩家(8.0版本也受影响)。祝你早日迎来20岁生日,也祝你2025年一切顺利。

各种脑洞调侃更是层出不穷:

[2013年5月8日 13:25] Oracle 的那些家伙可能收不到这些消息,因为他们的触发器没触发 😉
[2015年6月24日 21:42] 这个Bug今年秋季就要上中学了。
[2020年6月11日 21:36] 这个Bug比我年纪还大。
[2020年7月15日 12:28] 各位,我女朋友说等这个Bug解决了她就嫁给我。请问这Bug有什么进展吗?PS:我们从2017年就开始等了,她现在都考虑嫁给Gary了。PS 2:Gary你真是个混蛋!

还有网友好奇起了当年的Bug提交者:

[2019 年 11 月 11 日 9:12] 很想知道那位提交错误报告的人现在怎么样了。他还活着吗?还在用MySQL吗?

Omer Barnir亲自现身回复:

[2019年11月12日 14:29] 谢谢关心。我一切都好,而且正在使用MySQL。

更离奇的是,还有人写了一封“求婚情书”:

[2025年2月19日 19:19]亲爱的#11472,

二十年来,你始终屹立不倒——未解决,未触及,如同坚韧的丰碑,历经无数次更新。当其他问题来来去去时,你却始终如一,提醒着我,即便在充斥着外键和层层变化的时代,有些事情也永远不会如预期般发生。

从遇见你的那一刻起,我就知道你很特别,你是个未解之谜,一个值得拥抱的挑战。许多人试图修复你,但或许你注定无法被修复。或许,你注定要被爱。

所以,我单膝跪地:

我的亲爱的#11472,你愿意嫁给我吗?你愿意成为我永远未解决的问题吗?你愿意以某种方式闯入我的生活吗?即使它并非如潮水般涌来,而是以某种限制的方式?你愿意与我共创未来吗?就像外键更新一样,没有任何事物能够让你离开?

答应我,我将永不放手。答应我,我们将永远并肩而立——永远未解,永远相连。

永远属于你,

最忠诚的 MySQL 用户,绝不放手。

四、活了二十年的Bug,终于退场

整整二十年,它从一个功能缺陷,变成了程序员圈子“臭名昭著”的传奇老Bug。

在Reddit帖子的下方,有网友一语道尽这个Bug的地位:

真是不可思议,这玩意儿现在几乎成了互联网上的传奇故事。每隔几年就会有人重新发现它,拿它的年代开玩笑,信誓旦旦地说MySQL永远不会碰它,然后……它就像一张无人问津的亡灵票一样,一直活着。

今年,它终于迎来大结局:2026年3月,官方通过 WL#17024 修复了这个Bug。至此,这个跨越二十年、收获无数吐槽与玩梗的传奇Bug,终于画上句号。

有趣的是,盼了二十年的修复真的到来时,有网友却表示:

“搞什么鬼?我一直很依赖这个功能!请改回来。”

“什么?!那我们以后每年该给谁买蛋糕呢……”

来源丨网址:https://www.reddit.com/r/programming/comments/1tn3vfx/the_infamous_20_year_old_mysql_bug_11472_has_been/
整理丨dbaplus社群
*仅为提供参考和学习交流
转自:dbaplus社群

版权申明:内容来源网络,版权归原创者所有,如有侵权请联系删除

想了解更多干货,可通过下方扫码关注

可扫码添加上智启元官方客服微信👇

未经允许不得转载:17认证网 » 臭名昭著的、存在20年的MySQL Bug #11472终于被修复了
分享到:0

评论已关闭。

400-663-6632
咨询老师
咨询老师
咨询老师