MySQL 自增主键产生的空洞问题

1,603 阅读1分钟

在产品演示的时候,发现了这个BUG所导致的问题,场面一度十分尴尬,所以来记录一下这个问题。

场景还原

数据库引擎:InnoDB
数据库版本: MySQL 5.7.33
数据库相关配置:默认
场景:代码中连续插入多条数据(list),但这次上只插入了一条(list.size()==1)
image.png
这张表在没有发生delete的情况下,7468的下一条数据变成了7470,漏掉了7469!!!

Bug再现

这个SerialNo会作为关联项被存储到另一张表中,这时候**的事情就发生了。
image.png
代码中获取到了7469这个自增主键,但这时候通过7469去查关联的表,得到的结果就是no result

原因

在mysql5.1开始,新增加了一个配置项innodb_autoinc_lock_mode来设定auto_increment方式,可以设置的值为0,1,2,默认值为1。正是这个设定,产生了主键空洞。

模式0也就是传统的auto_increment方式,每次自增主键都会给数据库表加上一个auto-inc的锁(特殊的表级锁),该锁会持续到sql语句结束,而不是事务结束。

为了提高并发的效率,mysql5.1引入了全新的auto_increment方式。如果mode==1,那么在无法确定插入语句条数的情况下,mysql会预先分配一定的主键字段给语句,此时就有可能发生主键空洞!

Bug修复

  1. 修改innodb_autoinc_lock_mode模式为0,此时采用auto-inc来提供主键,可以有效避免主键空洞的问题,但对于并发量高的情况,可能不好。
  2. 修改代码,对于单行插入的情况,还是尽量不要去用list去插入。