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

图1
图1中是表t原有的数据,这个时候我们执行show create table t会看到如下输出,如图二所示现在的自增值是2,也就是下一个不指定主键值的插入的数据的主键就是2

图2
Innodb引擎的自增值,是保存在内存中的,并且到了mysql8.0版本后,才有了“自增值持久化”的能力,也就是才实现了"如果发生重启,表的自增值可以恢复为mysql重启前的值"
也就说在mysql5.7及之前的版本,自增值保存在内存中,并没有持久化,每次重启后,第一次打开表的时候,都会去找自增值的最大值max(id),然后讲max(id)+1作为当前表的自增值
举例来说,如果一个表中当前数据行的最大id=3,自增值=4,这个时候我们删除id=3的数据后,自增值还是=4,但是这个时候如果马上重启,重启后这个表的自增值就会变成3,
我们可以做个实验:


这个时候的自增值=4

我们删掉了id=3的数据
然后我们在查询一下自增值,我们可以看到这个时候的自增值还是4

然后我们在重启以后在查询一下,可以看到这个时候的自增值就是3了

在mysql8.0版本,将自增值的变更记录在了redo log中,重启的时候依靠redo log恢复重启之前的值
接下来我们在做个实验说明一下自增值为啥不是连续的,我们刚才建的表t的c字段我加了唯一索引,现在我们表中的自增值应该是3对吧,因为刚才我重启过了,然后我们执行INSERT INTO t VALUES(NULL,2,3)
这条语句,会报错插入失败,这条语句的执行流程是这样的
1,执行器调用innodb引擎接口写入一行,传入的这一行的值是(0,2,3);
2,innodb发现用户没有指定自增id的值,获取表t当前的自增值3;
3,将传入的行的值改成(3,1,1);
4,将表的自增值改成4;
5,继续执行插入数据操作,由于已经存在C=2的记录,所以报Duplicate key eror,语句返回

然后我们在查询一下自增值,看到了没有,现在的自增值已经变成了4,也就说上面这条语句插入失败以后,自增值没有回滚,这样下次我们在插入数据的时候的主键值就是4,就会造成我们的主键值不一致

所以说,唯一键冲突是导致自增主键id不连续的第一种原因
同样的,事务回滚也会产生类似的现象,这就是第二种原因
你可能会问,为什么在出现唯一键冲突或者事务回滚的时候,mysql没有把表t的自增值改回去呢?如果把表t的当前自增值从4改回3,再插入新数据的时候,不就可以生成id=3的一行数据了吗?
其实,mysql这么设计是为了提升性能,接下来,分析一下这个设计思路,看看自增值为什么不能回退
假设有两个并行执行的事务,在申请自增值的时候,为了避免两个事务申请到相同的自增id,肯定要加锁,然后顺序申请
1.假设事务A申请到了id=2,事务B申请到id=3,那么这时候表t的自增值是4,之后继续执行
2.事务B正确提交了,但事务A出现了唯一键冲突
3.如果允许事务A把自增id回退,也就是把表t的当前自增值改成2,那么就会出现这样的情况:表里面已经有id=3的行,而当前的自增id值是2
4.接下来,继续执行的其他事务就会申请到id=2,然后在申请到id=3,这时候,就会出现插入语句报错“主键冲突"
而为了解决这个主键冲突,有两种方法:
1,每次申请id之前,先判断表里面是否已经存在这个id,如果存在,就跳过这个id,但是,这个方法的成本很高,因为,本来申请id是一个很快的操作,现在还要再去主键索引树上判断id是否存在
2,把自增id的锁范围扩大,必须等到一个事务执行完成并提交,下一个事务才能再申请自增id,这个方法的问题,就是锁的粒度太大,系统并发能力大大下降
可见,这两个方法都会导致性能问题,造成这些麻烦的罪魁祸首,就是我们假设的这个”允许自增id回退“的前提导致的,
因此,innodb放弃了这个设计,语句执行失败也不回退自增id,也正是因为这样,所有才只保证了自增id是递增的,但不保证是连续的。
mysql的innodb自增主键为什么不是连续的的更多相关文章
- java面试一日一题:mysql中的自增主键
问题:请讲下mysql中的自增主键 分析:该问题主要考察对mysql中自增主键的掌握,使用场景及如何设置 回答要点: 主要从以下几点去考虑 1.什么自增主键 2.使用场景是什么: 3.innodb_a ...
- INNODB自增主键的一些问题 vs mysql获得自增字段下一个值
今天发现 批量插入下,自增主键不连续了....... InnoDB AUTO_INCREMENT Lock Modes This section describes the behavior of A ...
- INNODB自增主键的一些问题
背景: 自增长是一个很常见的数据属性,在MySQL中大家都很愿意让自增长属性的字段当一个主键.特别是InnoDB,因为InnoDB的聚集索引的特性,使用自增长属性的字段当主键性能更好,这里要说明下自增 ...
- mycat分布式mysql中间件(自增主键)
一.全局序列号 全局序列号是MyCAT提供的一个新功能,为了实现分库分表情况下,表的主键是全局唯一,而默认的MySQL的自增长主键无法满足这个要求.全局序列号的语法符合标准SQL规范,其格式为:nex ...
- 《Mysql - 自增主键为何不是连续的?》
一:自增主键是连续的么? - 自增主键不能保证连续递增. 二:自增值保存在哪里? - 当使用 show create table `table_name`:时,会看到 自增值,也就是 AUTO_INC ...
- Innodb自增主键与sql_mode
1.自增主键 1.设置自增主键 建表设置自增主键,设置自增主键需要唯一约束,否则会报错.(即指定列数据唯一) mysql> create table test_zz(id int auto_in ...
- MySQL 中的自增主键
MySQL 的主键可以是自增的,那么如果在断电重启后新增的值还会延续断电前的自增值吗?自增值默认为1,那么可不可以改变呢?下面就说一下 MySQL 的自增值. 特点 保存策略 1.如果存储引擎是 My ...
- mysql 自增主键为什么不是连续的?
由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑 MyISAM 引擎的自增值保存在数据文件中 nnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 ...
- MySQL 插入与自增主键值相等的字段 与 高并发下保证数据准确的实验
场景描述: 表t2 中 有 自增主键 id 和 字段v 当插入记录的时候 要求 v与id 的值相等(按理来说这样的字段是需要拆表的,但是业务场景是 只有某些行相等 ) 在网上搜的一种办法是 先获取 ...
随机推荐
- 什么是DA控制
液压系统中的DA控制: Automotive Drive control & Anti stall control 自动变速控制 & 防熄火控制 (DA控制) 1:自动(变速)驱动控制 ...
- MVC原理图解
注解: 视图中通过Action方法向控制器请求数据 控制器通过view()方法向视图呈现数据
- SpringMVC总结三:请求Controller返回视图类型以及请求方式、参数介绍
视图解析,请求Controller返回的视图类型: @Controller @RequestMapping("/test") public class TestController ...
- Windows:condition_variable 两个例子
题目:子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码.注意:一定是子线程先执行,主线程再执行. #in ...
- Linux常用基本命令 1
useradd 创建用户. password 修改密码. date 查看时间 man date 帮助文档.f往后翻 b往前翻 q退出.软修改 man hwclock 修改硬件时钟, cal 查看日历 ...
- Apache fcgistarter命令
一.简介 fcgistarter命令用于启动FastCGI程序. 二.语法 fcgistarter -c command -p port [ -i interface ] -N num 参考:http ...
- Mybatis——Spring整合
一.引入依赖 Spring的相关jar包 mybatis-3.4.1.jar mybatis-spring-1.3.0.jar mysql-connector-java-5.1.37-bin.jar ...
- css模块化思想(一)
什么是css模块化思想?(what) 为了理解css模块化思想,我们首先了解下,什么是模块化,在百度百科上的解释是,在系统的结构中,模块是可组合.分解和更换的单元.模块化是一种 处理复杂系统分解成为更 ...
- js 二维数组排序sort()函数
一.按数值排序 var arr = [[1, 2, 3], [7, 2, 3], [3, 2, 3]]; arr.sort(function(x, y){ return x[0] – y[0];}) ...
- Requests接口测试(五)
使用python+requests编写接口测试用例 好了,有了前几章的的基础,写下来我把前面的基础整合一下,来一个实际的接口测试练习吧. 接口测试流程 1.拿到接口的URL地址 2.查看接口是用什么方 ...