[MySQL Bug]bug#61209简析

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

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的值不会重叠了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值