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

前言

如果一个线程回滚,例如唯一键冲突的情况回滚时,回滚了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。

相关推荐
JavaPub-rodert26 分钟前
Redis 和 MySQL 如何保证数据一致性?从业务方案到底层原理完整讲解
数据库·redis·mysql
笑梦无境2 小时前
mysql的安装及配置(3)
数据库·mysql·adb
Gent_倪2 小时前
MySQL分库、分表、分区详解
数据库·mysql
Wang's Blog2 小时前
Vibe Coding一人即团队系列54:云服务器 Node.js 与 MySQL 9 环境搭建及配置指南
服务器·人工智能·mysql·node.js
数智启示录3 小时前
Apache Kafka 幂等 Producer 的边界:Exactly-Once 到了 MySQL 为什么失效 【Kafka合集】
数据库·经验分享·分布式·mysql·面试·kafka·apache
—Miss. Z—3 小时前
计算机三级数据库技术—填空题
数据库·mysql
Gent_倪5 小时前
MySQL 与 Hive 语法异同点详解
数据库·hive·mysql
老周聊架构5 小时前
Flink 连接器与生态:Kafka Offset 提交修复与 MySQL Binlog 延迟指标
mysql·flink·kafka
泡干脆面就番茄18 小时前
MySQL_子查询_分页查询与联合查询详解
数据库·mysql
天若有情67319 小时前
Node+MySQL小型全栈笔记项目实战课程分享
数据库·笔记·mysql