文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

回馈开源,我如何排查一个MySQL Bug

2024-04-02 19:55

关注

X-Engine是阿里巴巴自研的高性能低成本存储引擎,经过多年的努力,我们在集团内部以AliSQL(X-Engine)的形式(AliSQL是阿里的MySQL分支)支持了许多业务,为用户带来了显著的成本和性能收益。

时至今日,阿里巴巴数据库团队已经向MySQL官方提交了许多有价值的bug及修复方案。我们继承了这一优良传统,在生产、测试中遇到MySQL相关的问题,总是积极地思考解决方案,并迅速与官方交流沟通,为开源社区的发展贡献自己的力量。

下文将介绍我们刚发现的一个MySQL问题及修复方案。遇到相同情况的朋友需要注意了,也许不符合规范的数据已经写入你们的数据库了。

背景知识

如果MySQL参数sql_mode包含以下3项:

NO_ZERO_DATE

NO_ZERO_IN_DATE

STRICT_TRANS_TABLES

向DATE类型列,插入'0000-00-00'或者年/月/日3部分任意一部分为0,都将失败。

回馈开源,我如何排查一个MySQL Bug

异常:‘0000-00-00’竟然插入成功了

在MySQL 8.0.16上依次执行以下语句:

set sql_mode='';

create table test (mydate DATE NOT NULL DEFAULT '0000-00-00');
set sql_mode=default;

show variables like "sql_mode";

insert into test values();

select * from test;

这里先将sql_mode设为空的目的是:建表时将mydate的default value设为'0000-00-00',否则会因default value不符合NO_ZERO_DATE而建表失败。

建表成功后将sql_mode设回default,包含:

ONLY_FULL_GROUP_BY

NO_ZERO_DATE

NO_ZERO_IN_DATE

STRICT_TRANS_TABLES

ERROR_FOR_DIVISION_BY_ZERO

NO_ENGINE_SUBSTITUTION

然而,这时竟然成功向test库里插入了一条'0000-00-00'的DATE。显然,NO_ZERO_DATE的语义被打破了。

回馈开源,我如何排查一个MySQL Bug

抽丝剥茧,原来问题出在这

首先,我们定位到MySQL插入路径,检查default value是否合法的函数。

回馈开源,我如何排查一个MySQL Bug

这个函数比较简单,找出用户insert lists不包含的、且有default value的列,检查它们的default value是否合法。write_set是一个bitmap,标识了用户insert lists里包含哪些列。

我用gdb在该函数处加了断点,执行上述case,竟然发现write_set里全部bit被设为了1。这显然不正常的现象,我的插入SQL语句insert into test values();insert list明明为空,write_set全为0才合理。看来有函数错误地修改了它。

于是乎,我用gdb给write_set的地址加了一个watchpoint,重新执行insert语句。这次定位到了修改write_set的地方:

回馈开源,我如何排查一个MySQL Bug

该函数在检查default value是否合法前执行,其作用是当binlog_format为ROW且binlog_row_image为FULL时,write_set会被全部设置为1。

参数binlog_format指定了binlog格式,有三个备选项:

ROW代表主备之间通过log_event同步;

STATEMENT代表主备之间通过SQL语句同步;

MIXED则是混合格式,默认用STATEMENT方式,一些特殊情况下用ROW方式。

由于主备通过STATEMENT同步(虽然它产生的binlog数量小),可能因上下文信息、环境不同等因素,导致结果不一致,因此安全起见,binlog_format默认为ROW。

参数binlog_row_image指定了ROW格式binlog要记录哪些信息。它也有三个备选项:

FULL表示binlog记录变更前后的所有列;

MINIMAL表示binlog只记录唯一标识列和修改列;

NOBLOB表示BLOB是修改列或唯一标识列,才记录,其它列与FULL相同全部记录。

binlog_row_image默认为FULL。

当binlog_format为ROW且binlog_row_image为FULL 时,为了保证所有列都写到binlog里,write_set竟然被全部设置为1。

write_set变量本是用来标识用户插入列,又被赋予了控制写binlog的重任。多重语义交织,很容易出bug。这也给我们编码带来启示:每个变量应当有确切的语义。

修复建议

导致这个bug的原因是write_set用处太多。因此可以创建一个新的bitmap:binlog_write_set,专门用于控制写binlog。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯