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 的值相等(按理来说这样的字段是需要拆表的,但是业务场景是 只有某些行相等 ) 在网上搜的一种办法是 先获取 ...
随机推荐
- android 标签页<include /> 的使用
在android页面布局设计中,有时候需要用到很多相同的布局设计.如果每个用到该布局的xml里都写那个相同布局的话,会造成语句冗余,而且可读性很差. 为了解决这个问题的话,我们可以把相同布局的代码单独 ...
- Coins and Queries(codeforce 1003D)
Polycarp has nn coins, the value of the i-th coin is aiai . It is guaranteed that all the values are ...
- 【转】VS 安全开发生命周期(SDL)检查
前面在学习使用google的protobuf时在VS2012中一直无法编译编译通过,经过查找一些资料原来发现,并不是protobuf的问题,而是自己在使用VS2012时,没有完全了解VS2012的强大 ...
- WordPress,discuz 根据不同的入口url更换logo
Discuz!中调用cookie的思路出来了: 设置cookie:dsetcookie('cookie名', 'cookie值', '有效时间'); 读取cookie有两种方法,第一种使用get ...
- c++ 多态问题(在虚函数里调用虚函数)
最近在看cocos2d-x的源码,非常感激cocos2d作者的开源精神.在看代码的过程中感觉两个方向让我受益,1.把之前从书中看到的c++知识,明白了怎么运用.2.学习作者驾驭代码的巧妙方法. 看co ...
- win10 Kinect2 Visualstudio2015 opencv3环境搭建
1.下载kinect SDK ( Kinect for Windows SDK 2.0 ): https://www.microsoft.com/en-us/download/details.asp ...
- BeanUtils简单应用
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http:// ...
- 【linux命令】setterm控制终端属性命令(中英文)
[linux命令]setterm控制终端属性命令(中英文) 2018年03月23日 17:13:44 阅读数:489 标签: linux 更多 个人分类: linux 摘自:https://blog. ...
- 彻底清除Window7下的360注册表
当流氓360遇到强迫症,将注定有一场厮杀... 今天装了个虚拟机,其中win7系统是在网上随便下载了一个,是非纯净版的,自带了360在内的好多软件,其他软件都轻松删掉,但查看注册表时发现竟然有360残 ...
- 编写高质量代码改善C#程序的157个建议——建议5: 使用int?来确保值类型也可以为null
建议5: 使用int?来确保值类型也可以为null 基元类型为什么需要为null?考虑两个场景: 1)数据库中一个int字段可以被设置为null.在C#中,值被取出来后,为了将它赋值给int类型,不得 ...