首页 > 数据库 >面试官:MySQL 自增主键一定是连续的吗?大部分人都会答错!

面试官:MySQL 自增主键一定是连续的吗?大部分人都会答错!

时间:2023-06-25 09:44:11浏览次数:51  
标签:语句 insert 面试官 增值 答错 自增 主键 id

测试环境:

MySQL版本:8.0

数据库表:T (主键id,唯一索引c,普通字段d)

如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不能保证连续递增。

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

一、自增值的属性特征:

1. 自增主键值是存储在哪的?

MySQL5.7版本

在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值 max(id),然后将 max(id)+1 作为这个表当前的自增值。

MySQL8.0之后版本

在 MySQL 8.0 版本,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值。

可以通过看表详情查看当前自增值,以及查看表参数详情AUTO_INCREMENT值(AUTO_INCREMENT就是当前数据表的自增值)

2. 自增主键值的修改机制?

在表t中,我定义了主键id为自增值,在插入一行数据的时候,自增值的行为如下:

  1. 如果插入数据时 id 字段指定为 0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段;
  2. 如果插入数据时 id 字段指定了具体的值,就直接使用语句里指定的值。

根据要插入的值和当前自增值的大小关系,自增值的变更结果也会有所不同。假设,某次要插入的值是 X,当前的自增值是 Y。

  1. 如果 X<Y,那么这个表的自增值不变;
  2. 如果 X≥Y,就需要把当前自增值修改为新的自增值。

二、新增语句自增主键是如何变化的:

我们执行以下SQL语句,来观察自增主键是如何进行变化的

insert into t values(null, 1, 1);

流程图如下所示

流程步骤:

  • AUTO_INCREMENT=1 (表示下一次插入数据时,如果需要自动生成自增值,会生成 id=1。)
  • insert into t values(null, 1, 1) (执行器调用 InnoDB 引擎接口写入一行,传入的这一行的值是 (0,1,1))
  • get AUTO_INCREMENT=1 (InnoDB 发现用户没有指定自增 id 的值,获取表 t 当前的自增值 1 )
  • AUTO_INCREMENT=2 insert into t values(1, 1, 1) (将传入的行的值改成 (1,1,1),并把自增值改为2)
  • insert (1,1,1) 执行插入操作,至此流程结束

大家可以发现,在这个流程当中是先进行自增值的+1,在进行新增语句的执行的。大家可以发现这个操作并没有进行原子操作,如果SQL语句执行失败,那么自增是不是就不会连续了呢?

三、自增主键值不连续情况:(唯一主键冲突)

当我执行以下SQL语句时

insert into t values(null, 1, 1);

第一次我们可以进行新增成功,根据自增值的修改机制。如果插入数据时 id 字段指定为 0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段;

当我们第二次在执行以下SQL语句时,就会出现错误。因为我们表中c字段是唯一索引,会出现Duplicate key error错误导致新增失败。

例如:

  • AUTO_INCREMENT=2 (表示下一次插入数据时,如果需要自动生成自增值,会生成 id=2。)
  • insert into t values(null, 1, 1) (执行器调用 InnoDB 引擎接口写入一行,传入的这一行的值是 (0,1,1))
  • get AUTO_INCREMENT=2 (InnoDB 发现用户没有指定自增 id 的值,获取表 t 当前的自增值 2 )
  • AUTO_INCREMENT=3 insert into t values(2, 1, 1) (将传入的行的值改成 (2,1,1),并把自增值改为3)
  • insert (2,1,1) 执行插入操作,由于已经存在 c=1 的记录,所以报 Duplicate key error,语句返回。

可以看到,这个表的自增值改成 3,是在真正执行插入数据的操作之前。这个语句真正执行的时候,因为碰到唯一键 c 冲突,所以 id=2 这一行并没有插入成功,但也没有将自增值再改回去。所以,在这之后,再插入新的数据行时,拿到的自增 id 就是 3。也就是说,出现了自增主键不连续的情况。

四、自增主键值不连续情况:(事务回滚)

其实事务回滚原理也和上面一样,都是因为异常导致新增失败,但是自增值没有进行回退。

五、自增主键值不连续情况:(批量插入)

批量插入数据的语句,MySQL 有一个批量申请自增 id 的策略:

  1. 语句执行过程中,第一次申请自增 id,会分配 1 个;
  2. 1 个用完以后,这个语句第二次申请自增 id,会分配 2 个;
  3. 2 个用完以后,还是这个语句, 第三次申请自增 id,会分配 4 个;
  4. 依此类推,同一个语句去申请自增 id,每次申请到的自增 id 个数都是上一次的两倍。

执行以下SQL语句(在表t中先新增了4条数据,在创建表tt把表t数据进行批量新增)

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 tt like t;
insert into tt(c,d) select c,d from t;

insert into tt values(null, 5,5);

第一次申请到了 id=1,第二次被分配了 id=2 和 id=3, 第三次被分配到 id=4 到 id=7。当我们再执行 insert into t2 values(null, 5,5),实际上插入的数据就是(8,5,5),出现了自增主键不连续的情况。

六、自增主键值的优化

1.什么是自增锁

自增锁是一种比拟非凡的表级锁。并且在事务向蕴含了 AUTO_INCREMENT 列的表中新增数据时就会去持有自增锁,假如事务 A 正在做这个操作,如果另一个事务 B 尝试执行 INSERT语句,事务 B 会被阻塞住,直到事务 A 开释自增锁。

2.自增锁有哪些优化

在 MySQL 5.0 版本的时候,自增锁的范围是语句级别。也就是说,如果一个语句申请了一个表自增锁,这个锁会等语句执行结束以后才释放。显然,这样设计会影响并发度。在MySQL 5.1.22 版本引入了一个新策略,新增参数 innodb_autoinc_lock_mode,默认值是 1。

传统模式(Traditional)

这个参数的值被设置为 0 时,表示采用之前 MySQL 5.0 版本的策略,即语句执行结束后才释放锁;

传统模式他可以保证数据一致性,但是如果有多个事务并发的执行 INSERT 操作,AUTO-INC的存在会使得 MySQL 的性能略有降落,因为同时只能执行一条 INSERT 语句。

间断模式(Consecutive)

这个参数的值被设置为 1 时:普通 insert 语句,自增锁在申请之后就马上释放;类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放;

间断模式他可以保证数据一致性,但是如果有多个事务并发的执行 INSERT 批量操作时,就会进行锁等待状态。如果我们业务插入数据量很大时,这个时候MySQL的性能就会大大下降。

穿插模式(Interleaved)

这个参数的值被设置为 2 时,所有的申请自增主键的动作都是申请后就释放锁。

穿插模式他没有进行任何的上锁设置。在一定情况下是保证了MySQL的性能,但是他无法保证数据的一致性。如果我们在穿插模式下进行主从复制时,如果你的binlog格式不是row格式,主从复制就会出现不一致。

七、MySQL8.0做了哪些优化

在MySQL8.0之后版本,已经默认设置为 innodb_autoinc_lock_mode=2binlog_format=row.。这样更有利与我们在 insert … select 这种批量插入数据的场景时,既能提升并发性,又不会出现数据一致性问题。

版权声明:本文为CSDN博主「又 欠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_48157004/article/details/128356734

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

标签:语句,insert,面试官,增值,答错,自增,主键,id
From: https://www.cnblogs.com/javastack/p/17502154.html

相关文章

  • Go 匿名返回值的坑——这道题据说 90% 的同学都答错了!
    !https://zhuanlan.zhihu.com/p/639160955今天分享的内容比较基础,准确地说是Go的语言特性——命名、匿名返回值。先看下面的例子,猜测会输出什么?packagemainfuncmain(){ println(A()) println(B())}funcA()int{ variint deferfunc(){ i=i+3 }() r......
  • 以阿里社招Android面试为例,详讲面对面试官到面试中到面试结束
    前言今天有个小伙伴和我说,Android技术还行,主要是对面试没什么把握,小编想了想,不该是多半面试题都会迎难而解,怎么会什么把握呢?但仔细又一想,技术和面试也是两回事,技术可以也不代表面试就可以过,那咱们就来好好叨叨该如何去面试面对面试官我在网上看到了很多面试官的分享,他们面过上百人,......
  • ChatGPT常回答错误的几类问题
    下面几类问题,是ChatGPT常回答错误的。复杂的数学和科学问题GPT是一种基于统计模型的语言模型,它并不具备理解数学和科学概念的能力。因此,当问题涉及到深层次的数学和科学知识时,GPT可能会无法理解问题的本质,从而导致给出不确定或错误的答案。“MathPrompter:MathematicalReasoning......
  • 面试官:“同学,你做的这几个项目都不错。但怎么问QPS你就胡说呢?”
    作者:小傅哥博客:https://bugstack.cn沉淀、分享、成长,让自己和他人都能有所收获!......
  • Android面试「25K—30K」的坑位,面试官喜欢问些什么?
    前言掉帧监控,函数插桩,慢函数检测,ANR监控,启动监控……这些让Android开发者们头皮发麻的内容,如今可都成为了大厂中面试必问题目:用什么机制去监控,在哪里函数插桩,反射调用用哪个类哪个方法和哪个属性?这些问题恐怕是会难倒一大批向高阶进军的开发者。目前大公司的app开发都要基于模块化......
  • Android面试官问的一些问题,看完这一篇就没有拿不到的offer
    背景我是2020年毕业于中南大学的计算机学院的,我毕业之后在华为工作了差不多两年多,一直都从事着Android开发。然后去年年底的时候因为我自己的一些原因打算离职到外面看看,那个时候我是投了超级多简历,然后去面试了小红书啊、快手啊,爱奇艺啊,微信,小米…等等很多的大厂,小厂,然后下面这......
  • 【后端面经】MySQL主键、唯一索引、联合索引的区别和作用
    目录0.简介1.主键2.唯一索引3.联合索引4.索引对数据库操作的影响5.其他索引5.1普通索引5.2全文索引5.3前缀索引6.总结7.参考资料0.简介索引是一类特殊的文件,用来存储检索信息,使数据库查找更加快速。1.主键主键是一类特殊的唯一索引,选择某一列元素作为主键,用来表......
  • 【后端面经】MySQL主键、唯一索引、联合索引的区别和作用
    (【后端面经】MySQL主键、唯一索引、联合索引的区别和作用)0.简介索引是一类特殊的文件,用来存储检索信息,使数据库查找更加快速。1.主键主键是一类特殊的唯一索引,选择某一列元素作为主键,用来表示每一行元素的特殊性,其特点如下在一个数据表中只有一个主键;主键不能为空(null);......
  • KingbaseES数据库分区表添加主键与索引的建议
    一、初始化测试环境#数据库版本信息KingbaseESV008R006C007B0012onx86_64-pc-linux-gnu,compiledbygcc(GCC)4.1.220080704(RedHat4.1.2-46),64-bit1.创建分区表:createtabletb(idbigint,statdate,nobigint,pdatedate,infovarchar2(50))partitionbyra......
  • 软件测试|SQL分类大概有几种?SQL中什么是主键和外键,它们之间的区别是什么?
    前言之前一个朋友面试测试开发岗位,面试官问了这个问题,朋友觉得自己没有很好回答这个问题,面试结束之后找到我,我只能帮他总结成这样了,希望能够帮助到那位朋友。获取更多技术资料,请点击!SQL语言分为五大类:DDL(数据定义语言)-Create、Alter、Drop这些语句自动提交,无需用Commit......