[MySQL Bug]bug#61209简析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介:

—————————————————

MySQL bug#61209,changelog如下,在5.5.23被fix

InnoDB: Running concurrent bulk inserts on a server with auto_increment_offset=1,auto_increment_increment greater than 1, and innodb_autoinc_lock_mode=1 could result in intermittent errors like the following, even with the primary key set to auto_increment and omitted from the INSERT statement:

 

Duplicate entry 'value' for key 'PRIMARY'

The workaround was to set auto_increment_offset=1 or innodb_autoinc_lock_mode=0 (“traditional”). (Bug #13817703, Bug #61209)

 

之前已经介绍过了innodb 如何处理auto inc的代码流程,现在来看看在innobase_next_autoinc里是如何来计算的。

假定我们设置:

set global  auto_increment_increment=3;

set global auto_increment_offset=2;

例如对于一条简单的SQL:insert into tt values (NULL),(NULL)

 

函数innobase_next_autoinc的参数:

–current   当前的值                                     (1)

–increment  申请保留的区间长度                  (6)

–offset      auto_increment_offset的值         (2)

–max_value    该自增列类型最大容许的值      (2147483647)

 

1.当offset > increment时,设置offset=0,也就是忽略掉auto_increment_offset

2.确定next_value的值

a.当max_value <= current时,next_value = max_value; (溢出)

b.当offset<=1时(1或0视为等同):

如果max_value – current <= increment ,  next_value = max_value; (溢出)

否则next_value = current + increment 

c.当max_value > current,且offse>1时

next_value = ((|current – offset| / increment) + 1)*increment+offset

可以看出来代码逻辑还是很简单的

现在在来看看test case

session 1

SQL1:insert into tt values (NULL),(NULL);

{interval_min = 2, interval_values = 2, interval_max = 8, next = 0x0}

innobase_next_autoinc的返回值为((1-2)/6+1)*6+2=8

table->autoinc=8

插入记录2,5

SELECT GET_LOCK(‘a’, 100);

session 2

SQL2:INSERT INTO tt (a) VALUES (NULL), (NULL), (NULL + GET_LOCK(‘a’, 1800));  

{interval_min = 8, interval_values = 3, interval_max = 17, next = 0x0}

调用函数innobase_next_autoinc两次

第一次返回值((8-2)/9+1)*9+2=11

table->autoinc=11

第二次调用是在写第3个记录时(在write_row函数中调用)

返回值为(((table->autoinc-auto_increment_offset)/auto_increment_increment+1))* auto_increment_increment+ auto_increment_offset

=((11-2)/3+1)*3+2=14

table->autoinc=14

在尝试插入第三个记录时会block住

session 1

SQL3:INSERT INTO tt (a) VALUES (NULL), (NULL), (NULL);

{interval_min = 14, interval_values = 3, interval_max = 23, next = 0x0}

同样调用函数innobase_next_autoinc两次

第一次返回值为20

第二次返回值为24

插入记录14,17,20

而每个预留区间的最小值是和table->auto_inc相关的,显然这里自增值发生了错乱,SQL3应该从17开始而不是14

官方patch请点击这里

主要修改了innobase_next_autoinc函数

SQL1的返回值为8

SQL2的返回值为17

SQL3的返回值为26

实际上稍微扩大了table->auto_inc的值,以避免出现交叉。加上patch后的innobase_next_autoinc函数的参数修改为:

–current     当前值

–need         需要自增值的个数

–step          auto_increment_increment

–offset        auto_increment_offset

–max_value   该自增类型的最大可能值


next_value=(|offset – current| / step)

next_value*=step

next_value+=( need * step +offset)

因此

 

SQL1的返回值为8 (|2-1|/3=0; 0*3; 0+2*3+2=8)

SQL2的返回值为17(|2-8|/3=2;2*3=6;6+3*3+2=17)

SQL3的返回值为26(|2-17|/3=5;5*3=15;15+3*3+2=26)

 

这样就可以保证table->autoinc的值不会重叠了。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
(十八)MySQL排查篇:该如何定位并解决线上突发的Bug与疑难杂症?
前面《MySQL优化篇》、《SQL优化篇》两章中,聊到了关于数据库性能优化的话题,而本文则再来聊一聊关于MySQL线上排查方面的话题。线上排查、性能优化等内容是面试过程中的“常客”,而对于线上遇到的“疑难杂症”,需要通过理性的思维去分析问题、排查问题、定位问题,最后再着手解决问题,同时,如果解决掉所遇到的问题或瓶颈后,也可以在能力范围之内尝试最优解以及适当考虑拓展性。
495 3
【Bug解决】Thinkphp5 PDO::__construct(): MySQL server has gone away解决办法
【Bug解决】Thinkphp5 PDO::__construct(): MySQL server has gone away解决办法
207 0
故障案例:MySQL唯一索引有重复值,官方却说This is not a bug
故障案例:MySQL唯一索引有重复值,官方却说This is not a bug
313 0
这次被坑惨了,MySQL的隐式转换导致了一个线上BUG
某一天,开发问我,为什么针对一个查询会有两条记录,且其中一条记录并不符合条件select * from tablea where xxno = 170325171202362928;xxno为 170325171202362928 和 170325171202362930的都出现在结果中。 一个等值查询为什么会有另外一个不同值的记录查询出来呢? 我们一起来看看究竟!
MySQL慢查询日志配置与简析
MySQL慢查询日志配置与简析
175 0
MySQL 5.6 change buffer bug导致crash
Insert buffer 内部标识长度的位图没有正确更新,导致问题
200 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等