自增主键为什么不是连续的?

前言

如果一个线程回滚,例如唯一键冲突的情况回滚时,回滚了sql语句,但是并没有把自增的值也-1。那么就会导致下一条插入的数据自增id出现了跳跃。


自增主键为什么不是连续的?

执行时机

mysql的自增主键+1的时机是在mysql真正执行之前,获取当前值并加1。

为什么自增主键不是连续的

如果一个线程回滚,例如唯一键冲突的情况回滚时,回滚了sql语句,但是并没有把自增的值也-1。那么就会导致下一条插入的数据自增id出现了跳跃。

为什么不回滚自增主键

之所以不让自增主键回滚是因为:

  1. 假如两个线程一个获取了id =2 ,另一个是id =3 。id=3的完成之后,如果id=2的需要回滚,那么后面再来一个插入请求,就会获取id=2的值,然后再来一个数据就会获取3从而导致主键冲突。
    如果要解决这个冲突:
    1. 就要每次插入时还要去数据库里查询一下是否有这个自增主键。这是非常耗时的,本来一个非常简单的操作,现在就会变得非常复杂。
    2. 还有一种方法就是扩大每次获取自增值的锁时间,对于自增值的锁一般是很快的获取之后+1然后就会释放。如果要防止上面的冲突,就是等这个sql执行完成之后,再允许其它线程获取这个锁。这显然也会大大降低并发值,因此mysql放弃了连续。

innodb_autoinc_lock_mode

那么后来mysql添加了一个参数innodb_autoinc_lock_mode,

  1. 如果设置成0:就是会等一个写入执行完成之后,再让另一个线程获取自增的值
  2. 如果设置成1:那么对于单个的insert值,就会获取到自增值就直接释放。但是对于批量写入或者批量替换的请求,那么就会等这个操作执行完成之后,再让另一个线程获取自增值。
  3. 如果设置成2,那么所有的insert操作都会获取到自增值之后直接释放。不等sql执行结束。

设置成2导致的问题

如果再批量插入A的时候,id是自增1,2,3,4,6,7,另一个线程B也插入了一条数据5,

但是在写入binlog的时候,是一个线程一个线程写入的。

那么线程B插入的数据对应的id只能是线程A开头的或者结尾,如果binlog的format= statement。就会导致主备数据不一致。
所以这这又是一个将binlog的format= row的原因。

这里需要注意 如果线程A不是批量写入的语句。只是一个普通的insert语句ID 1,然后又有另一个线程B获取自增建ID 2。但是再写入binlog时,线程B先写入了,然后线程A写入。

这种情况下 即使binlog的format=statement 也不会导致主备数据的数据不一致。在binlog中还有一个步长的参数,在执行线程B时,步长会为2,ID还是2,执行线程A时,步长是1,ID还是1。

相关推荐
Ytadpole17 小时前
MySQL 数据库优化设计:优化原理和数据库表设计技巧
数据库·mysql·优化·索引·查询·检索·表设计
Boop_wu18 小时前
[MySQL] 基础操作
数据库·mysql
小糖学代码20 小时前
MySQL:14.mysql connect
android·数据库·mysql·adb
可观测性用观测云1 天前
阿里云 RDS MySQL 可观测性最佳实践
mysql
白云偷星子1 天前
MySQL笔记14
数据库·笔记·mysql
绵绵细雨中的乡音1 天前
MySQL 常用函数实操指南:从基础到实战案例
数据库·mysql
凉栀お_1 天前
MySQL相关知识查询表中内容(第二次作业)
数据库·mysql
Java水解1 天前
【SQL】MySQL中空值处理COALESCE函数
后端·mysql
shuair1 天前
mysql8支持远程访问 -mysql5.7支持远程访问
linux·mysql
白云偷星子1 天前
MySQL笔记13
数据库·笔记·mysql