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

在第4篇文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑。

但实际上自增主接口类型键不能保证连续递增。

今天这篇文章,我们就来说说这个问题,看看什么情况下自增主键会出现 “空洞”?为了便于说明,我们索引超出了数组界限什么意思创建一个表t,其中id是自索引的作用增主键接口和抽象类的区别字段、c是唯一索接口测试引。

CREATE TABLE `t` (
`id` int(11) NOTNULLAUTO_INCREMENT,
`c` int(11) DEFAULTNULL,
`d` int(11) DEFAULTNULL,
PRIMARY KEY (`id`),
UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB;

自增值索引索引页是哪一页类型保存MySQL在哪儿?

在这个空表t里mysql数据算法分析的目的是库命令大全算法的有穷性是指面执行insert into t valu索引的优缺点es(null, 1, 1mysql密码忘记了怎么办);插入一行数据,再执行showcreate事务的四个特索引失效 table命令,就可以看到接口和抽象类的区别如下图所示的结果:

MySQL实战45讲_39 | 自增主键为什么不是连续的?

可以看到,表事务所所长的委托怎么接事务索引是什么意思里面出现了一个mysql索引算法导论AUTO_INCREMENT=2事务性工作是什么意思,表示下一次MySQL插入数据时,如果需要自动生成自增值,会生成id=2。

其实,这个输出结果容易引起这mysql数据库样的误解:自增值是保存在接口英文表结构定义里的。实际上,表的结构定义存放在后缀名为.frm的文件中,但是索引是什么意思并不会保算法分析的目的是存自增值。接口文档

不同的引擎对于自增值的保存策略不同。

  • MyISAM引擎的自事务文书增值保存在数据文件中。
  • InnoDB引擎的自增值,其实是事务的四个特性保存在了内存里,并且到了MySQL 8.0版本后,才有了“自增值持算法设计与分析久化”的能力,也就是才实现了“如果发生重启,表的自增值可以恢复为MySQL重启前的值”,具体情况是:
  1. MySQL 5.7mysql数据库命令大全之前的版本,自增值保存在事务内存里,并没有持久化。每次重启后,第一次事务所所长npc打开表的时候,都会事务的意思去找自增值的最大值max(id接口测试面试题),然后将max(id)+1作为这个表当前的自增值。
  2. MySQL 8.0mysql数据库本,将自增值索引的优缺点的变更记录在了re接口事务的四个特性crc错误计数do lo事务所是干什么的g中,重启的时候依靠redo log恢复重启之前的值。

理解了MySQMySQLL对自增值的保存策略以后,我们再看看mysql数据库自增值修改机制。算法的五个特性

自增值修改机制

在MySQL里面,如果字段id被定义为mysq接口测试面试题l怎索引下推么读AUTO_INCREMENT,在插入一行数据的时候,自增值的行为如下:

  1. 如果插入数据时id字段指定mysql增删改查语句为0、null 或未指定值,那么就把这个表当前的AUTO_INCREMENT值填到自接口英文增字段索引失效的几种情况
  2. 如果插入数据时id字段指定了具体的值,接口文档就直接使用语句里指定的值。

根据要插入的值和当前自增值的大小关系,自增值的变更结果也会有接口类型所不同。假设,某次要插mysql数据库基础知识入的值是X,当前的自增值是Y。

  1. 如果X<Y,那么索引页是哪一页这个表的自增值不算法变;
  2. 如果X≥Y,就需要把当前索引失效的几种情况自增值修改为新的自增值。事务所所长

新的自增值生成算法是:从auto_increment_offset开始接口卡,以autmysql数据库命令大全o_increment_increment为步长,持续叠加,直到找到第事务所一个大于X的值,作为新的自增值。接口和抽象类的区别

其中,auto_increment_offsmysql数据库基础知识etauto_increment_increment算法导论是两个系统参数,分别用来表示自增的初始值和步长,默认值都是1。

当auto_in索引的作用crement_offset和auto_increment_inc事务所是干什么的rement都是1的时候,新的自增值生成逻辑很简单,就是:

  1. 如果准备插入的值>=当前自增值,新接口测试面试题的自增值接口就是“准备插mysql安装配置教程索引符号的值+事务所所长npc务所是干什么的1”;
  2. 否则,自增值不变。

这就引入了我们文章开头提到的问题,在这两个参数都设置为1的时候,自增主键id却不能保证是连续的,这是什么原因呢?

自增值的索引失效修改时机

要回答这个问题,我们就要看一下自增值的修改时事务隔离级别机。假设,表t里面已经有了(1,1,1)这条记录,这时我再执行一接口测试面试题条插入数据命令:

insert into t v索引类型alues(null, 1, 1);

这个语句的执行流程就是:

  1. 执行器调用InnoDB引擎接口写入一行,传入的这一行的值是(0,1算法分析的目的是,1);
  2. In算法设计与分析noDB算法是什么发现用户没有指定自增id的值,获取表t当前的自增值2;
  3. 将传入的行的值改成(2,1,1);
  4. 将表的自增值改接口英文成3;
  5. 继续执行插入数据操作,由事务文书于已经存在c=1算法的时间复杂度取决于的记录,所以报Duplicate keyerro事务r,语句返回。

对应的执行流程图如下:

MySQL实战45讲_39 | 自增主键为什么不是连续的?

可以看到,这个表的自增值改成3,是在真正执行插入数据的操作之前。这个语句事务所是干什么的真正执行的时候,因为碰到唯一键c冲突,所以id=2这一行并没有插入成功,但也没有将自增值再mysql数据库基础知识改回去

所以,在这之后,再插入新的数据行时,拿到的自增id就是3。也就是说mysql安装,出现了自增主键不连续的情况。如图所示就是完整的演示结算法的五个特性果。

MySQL实战45讲_39 | 自增主键为什么不是连续的?

可以看到,这个操作序列复现了一个自增主键id不连续的现场(没有id=2的行)。

  • 唯一索引口crc错误计数事务所是干什么的冲突是导致自增主键id不连续的第一种原因。
  • 同样地,事务回滚也会产生类似的现象,这就是第二种原因。

下面这个语句序列就可以构造不事务所连续的自增id。

insert into t values(null,1,1);
begin;
insert into t values(null,2,2);
rollback;
insert into t values(null,2,2);
//插入的行是(3,2,2)

为什么在出现唯一键冲突或者回滚的时候,M接口ySQmysql数据库基础知识索引失效的几种情况L没有把表t的mysql索引自增值改回去呢?

如果把表t的当前自增mysql密码忘记了怎么办值从3改回2,再插入新数据的时候,不就可以生成id=2的一行数据了吗?

其实,MySQL这么设计是为了索引的优缺点提升性能。接下来,我就跟你mysql面试题分析一下这个设计思路,看看自增值为什么不能回退。

假设有两个并行执行的事务,在申请自增值的时候,为了避免两个事务申算法请到相同的自增id,肯定要加锁,然后顺序申请索引失效的几种情况

  1. 假设事务A申请到了id=2,接口 事务B申请到id=3,那么这时候表t的自增值是4,mysql怎么读之后继续执行。
  2. 事务B正确提交了,但事务A出现了唯一键冲突。
  3. 如果允许事务A把自增mysql密码忘记了mysql安装怎么办id回退,也就是事务性工作是什么意思接口测试用例设计表t的当前自增值改回2事务所所长npc,那么就接口和抽象类的区别会出现这样的情况:表里面已经有id=3的行,而当前的自增id值是2。
  4. 接下来,继续执行的其他事务就会申请到id=2,然后再申请到id=3。这时,就会出现插入语句报错“主键冲突mysql怎么读”。

而为了解决这个主键冲突,有两种方法:

  1. 每次申请id之前,先判断表里面是否已经存在这个id。如果存在,就跳过这个id。但是,这个方法的成本很高。因为,本来申请id是一个很快的mysql安装操作,现在还要再去主键索引树上判断id是否存在。
  2. 把自增id的锁范围扩大,必须等到一个事务执行接口测试用例设计完成并提交,MySQL下一个事务才能再申请自增id算法设计与分析。这个方法的问题,就是锁的粒度太大,系统并发能力大大下降。

可见,这两个方法都会导致性能问题。造成这些麻烦的罪魁祸首,就是我们假设的这个“允许自增id回退”的前提导接口测试面试题致的。

因此,mysql怎么读Inno算法DB放弃了这个设计,语句执行失败也不回退自增id。也正是因为这样,所以才只保证了自增id是递mysql怎么读增的,但不保证是连续的

自增锁的优化事务文书

索引失效的几种情况以看到,自增id锁并不是一个事务mysql安装锁,而是每次申请完就马上释放,以便允许别的事务再申请

其实,在MySQL 5.1版本之前,并不是这索引样的接口类型

在MySQL 5.0版本的时候,自增锁的范围是语句级别。也就是说,如果一个语句申请了一个表自增锁,这个锁会等语句mysql索引执行结束以后才释放。显然会影响并发度。

MySQL 5.1.22版本引入了一个新策略,新增参数innodb_autoinc_lo索引的作用接口英文ck_mode,默认值是1。

  1. 这个参数的值被设置为0时,表示采用之前MySQL 5.事务的意思0版本的策略,即语句执行结束后才释放索引的作用锁;
  2. 这个参数的值被设置为1时接口测试面试题
  • 普通insert语句,自增锁在申请之后就马上释放;
  • 类似insert …select这样的批量插入算法的五个特性数据的语句,自增MySQL接口类型还是要等语句结束后才被释放;
  1. 这个参数的值mysql数据库命令大全被设置为2时,所有算法的时间复杂度取决于的申请自增主键的动作都是mysql数据库命令大全申请后就释放锁。

你一定有两个疑问:为什么默认设置下,insert …select 要使用语句级的锁?为什么这个参数的默认值不是2?答案是,这么设计还是为了数据一致性

我们一起来看一下这个场景:

MySQL实战45讲_39 | 自增主键为什么不是连续的?

在这个例子里,我往接口测试用例设计表t1中插入了4行数据,然后创索引下推建了一个相同结构的表t2,然后两个session同时执行向表t2中插事务所是干什么的入数据的算法是什么操作。

你可以设想一下,如果session B是申请了自增值以后马上事务性工作是什么意思就释放自增锁,那么就可能出现这样算法的有穷性是指的情况:

  • sess索引失效的几种情况ion B先插事务入了两个记录,(1事务隔离级别,1,1)、(2,2,2);
  • 然后,session A来申请自增id得到id=3,插入了(3,5事务所,5);
  • 之后,session B继算法的特性续执行,插入两条记录算法的有穷性是指(4,3,3)、 (5,4,4mysql索引)。

你可能会说,这也没关系吧,毕竟ses接口卡sion B的语义本事务性工作是什么意思身就没有要求表t2索引的优缺点算法设计与分析所有行的数据都跟session A相同。

是的,从数据逻辑算法工程师上看是对的。但是,如果我们现在的binlog算法导论_formatmysql数据库=statement,你可以设想下,bin算法的有穷性是指log会怎么记录呢?

由于两个MySQLs接口和抽象类的区别ession是同时执行插入数据命令的,所以binlog里面对表t2的更新日志只有两种情况:要么接口测试先记session A的,要么先记sessionMySQL B的。

但不论是哪一种,这个binlog拿去从库执行,或者用算法的特性来恢复临时事务隔离级别实例,备库和临时索引超出了数组界限什么意思实例里面接口测试用例设计,session B这个语句执行出mysql数据库来,生成的结果里面,id都是连续的。这时,这个库就发生了数据不一致。接口测试面试题

出现数据不一致的原因是什么?

其实是因为原库session B的in接口mysql数据库命令大全和抽象类的区别sert语句,生成的id不连续。这个不连续的id,用statement格式的binlog来串行执行,是执行不出来的

而要解决这个问题,有两种思路:

  1. 一种思路是,让原库的批算法的事务的意思有穷性是指量插入数据语句,固定生成连续的id值。所以,自增锁直到语句执行结束才释放,就是为了达接口英文到这个目的。
  2. 另一种思路是,在binlog里面把插入数据的操算法的时间复杂度取决于作都如实记录进来,到备库执行事务隔离级别的时候,不再依赖于自增主键去生成。这种情况,其实就是innodb_autoinc算法_loMmysql数据库ySQLck_mode设置为2,同时binlog_format设置为r接口测试用例设计ow。

因此在生产上,尤其是有insert …索引下推select这种批量插入数据的场景时,从并发插入数据性能的角度考虑,我建议你这样设置:innodb_autoinc_lock_mode=2 ,并且 binlog_format=接口英文r索引的优索引失效缺点ow.mysql面试题

这样做,既能提升并发性,又不会出现数据一致性问题。 需要注意的是,我这里说的批量插入数mysql数据库基础知识据,包含的语句类型是insert …select、replace … select和load data语句。

但是,在普通的insert语句里面包含多个value值的情况下,即使innodb_autoinc_lock_mode设置为1,也不会等语句执行完成才算法分析的目的是释放锁。因为这类语索引失效句在申请自增id的时候,是可以精确索引页是哪一页计算出需要多索引少个id的,然后一次性申请,申请完成后锁就可以释放了。

也就是说,批量插入数据的语句,之所以需要这么设置,是因为“不知道要预先申请多少个id”。

既然预先不知道要申请多少个自增id,那么一种直接的想法就是需要事务的四个特性索引符号个时申请一个。但如果一个select索引的优缺点 …insert语句要插入1事务所mysql安装所长0万行数据,按照这个逻辑的话接口测试用例设计就要申请算法设计与分析10万次。显索引下推然,这种申请自增id的策略,在大批量插入索引失效的几种情况务的四个特性数据的情事务文书索引超出了数组界限什么意思况下算法接口的有穷性是指,不但速度慢,还会索引页是哪一页影响并发插入的性能算法工程师

因此,对于批量插入数据的语句,MySQLmysql数据库命令大全索引失效的几种情况一个事务性工作是什么意思接口文档量申请自增id的策略:

  1. 语句执行过程中,第一次申请自增id,会分配1个;mysql索引
  2. 1个用完以后,这个语句第二次申请自算法工程师务所所长增id,会分配2个;
  3. 2个用完以后,还是这个语句,第三次申请自增索引符号id,会分配4个;
  4. 依此类推算法导论,同一个语句去申请自增id,每次申请到的自增id个数都是上一次的两倍。

举个例子,我们一起看看下面的这个语索引失效接口测试面试题句序列:

insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);
create table t2 like t;
insert into t2(c,d) select c,d from t;
insert into t2 values(null, 5,5);

insert…select,实际上往表t2中插入了4行数据。但是,这四接口和抽象类的区别行数据是分三次申请的自增id,第一次事务的四个特性申请到了id=1,第二次被分算法的特性配了算法的五个特性id=算法2和id=3, 第三次算法算法的有穷性是指的有穷性是指被分配到i事务d=4到i索引失效d=7。

由于这条语句实际只用上了4个imysql安装d,所以id=5到idmysql安装配置教程=7就被浪费掉了。之后,再执行insert imysql面试题ntmysql安装配置教程o t2valuemysql索引s(null, 5,5),实际上插入的数据就是(8,5,算法导论5)。

这是主键id出现自增id不连续的第三种原因。