加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

回馈开源,我怎样排查一个MySQL Bug

发布时间:2022-03-23 04:53:36 所属栏目:编程 来源:互联网
导读:X-Engine是阿里巴巴自研的高性能低成本存储引擎,经过多年的努力,我们在集团内部以AliSQL(X-Engine)的形式(AliSQL是阿里的MySQL分支)支持了许多业务,为用户带来了显著的成本和性能收益。 时至今日,阿里巴巴数据库团队已经向MySQL官方提交了许多有价值的
       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插入路径,检查default value是否合法的函数。
 
这个函数比较简单,找出用户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的地方:
  
该函数在检查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。

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读