前几天偶然看到大家在讨论一道面试题,而且答案也不够统一,我感觉蛮有意思,在此就做一个解读,整个过程中确实会有几处反转。

作者:杨建荣的学习笔记来源:今日头条

前几天偶然看到大家在讨论一道面试题,而且答案也不够统一,我感觉蛮有意思,在此就做一个解读,整个过程中确实会有几处反转。

我们先来看下题目:

一张表,里面有ID自增主键,当insert了17条记录之后,删除了第15,16,17条记录,再把MySQL重启,再Insert一条记录,这条记录的ID是18还是15.

和后面的一些题目整体来看,难度不大,都是一些看起来很基础的问题,但是这道题目引起了我的注意,因为这道题目的背景过于开放,所以答案也是不固定的,而这也是我们在技术学习中需要保持的严谨态度。

首先这道题整体来看,想表达的是对于MySQL中自增列的理解。

按照我们常规理解的逻辑,ID自增,应该是18,按照这个逻辑怎么都不应该是15吧?

但是这个答案对吗?显然不是,我们进入第一轮反转。

确实,对于自增列的问题,这个是MySQL里面饱受诟病的老问题了。如果节点重启,会从数据列中按照max(id)+1的方式来处理,在多环境历史数据归档的情况下,如果主库重启,很可能会出现数据不一致的情况,记得在MySQL bug中很多人留言,说十多年前的老问题了,怎么还不解决。

而在OpenWorld上面Percona CEO Peter也再次提到了这个问题。

我认真查了一下这个bug的历史,巧合的是,这个问题是Peter在十几年前提出的,时光荏苒,一直没有修复。

好的,按照MySQL bug的思路来理解,答案应该是15了。

但是这个答案对吗?显然不是,我们进入第二轮反转。

这个题目的背景是不够清晰的,这个表的存储引擎没有说是InnoDB还是MyISAM,所以存在不确定性,这么说的意义在于,自增列的信息在MyISAM和InnoDB中的维护逻辑是不大一样的,在MyISAM中是存储持久化在文件中的,当数据库重启之后,是可以通过持久化的信息持续对ID进行自增的,而InnoDB的自增列信息既不在.frm文件,也不在.ibd文件中,所以在此启动的时候会按照max(id)+1的算法进行修复。

所以如果是MyISAM,则答案应该是18,而如果是InnoDB,则答案是15。

我们可以综合对比,用一个小的测试来模拟复现,我们选择的是MySQL 5.7环境。

为了对比明显,我们创建两张表test_innodb和test_myisam,分别对应InnoDB和MyISAM存储引擎,来做同样的操作,看看重启后的差异情况。

>>create table test_innodb(id int primary key auto_increment,name varchar(30))
engine=innodb;
>>create table test_myisam(id int primary key auto_increment,name varchar(30)) engine=myisam; 

插入几行数据,查看数据:

>>insert into test_innodb(name) values('aa'),('bb'),('cc');Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
>>insert into test_myisam(name) values('aa'),('bb'),('cc'); Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0 

查看两张表的数据情况,数据是完全一样。

>>select *from test_innodb;+----+------+| id | name |+----+------+| 1 | aa || 2| bb || 3 | cc |+----+------+3 rows in set (0.00 sec)
>>select *from test_myisam;+----+------+| id | name |+----+------+| 1 | aa || 2| bb || 3 | cc |+----+------+3 rows in set (0.00 sec)

在1,2,3的基础上,我们继续插入值为5,跳过id值为4。

>>insert into test_innodb(id,name) values(5,'ee');Query OK, 1 row affected (0.00 sec)
>>insert into test_myisam(id,name) values(5,'ee'); Query OK, 1 row affected (0.00 sec) 

此时查看test_innodb自增列已经开始增长,值为6。

>>show create table test_innodb\G CREATE TABLE `test_innodb`
( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB
AUTO_INCREMENT=6 DEFAULT CHARSET=utf81 row in set (0.00 sec) 

删除id=5的记录

delete from test_innodb where id=5;Query OK, 1 row affected (0.01 sec) 

删除记录之后,自增列还是保持不变。

show create table test_innodb\G CREATE TABLE
`test_innodb` ( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT ,
PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf81 row in set (0.00 sec) 

同理test_myisam也做同样的测试,结果是完全一样的,在此略过日志。

我们停止数据库

>>shutdown;Query OK, 0 rows affected (0.00 sec) 

重启数据库

#mysqld_safe --defaults-file=/data/mysql_5723/my.cnf & 

此时查看test_innodb和test_myisam的自增列就开始出现差异了。

MyISAM存储引擎的表test_myisam的自增列还是不变,为6。

show create table test_myisam\G CREATE TABLE `test_myisam` ( `id` int(11) NOT AUTO_INCREMENT,
`name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=6DEFAULT CHARSET=utf81 row in set (0.00 sec) 

而InnoDB存储引擎的表test_innodb的自增列却变为了4。

>>show create table test_innodb\G ***************************
1. row *************************** Table: test_innodbCreate Table:CREATE TABLE `test_innodb`( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT ,
PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 

我们继续插入一条数据,保持id列自增。

>>insert into test_innodb(name) values('ee');Query OK, 1 row affected (0.00 sec)
>>insert into test_myisam(name) values('ee'); Query OK, 1 row affected (0.00 sec) 

可以看到两张表的id列已经分道扬镳了。

>>select *from test_innodb; +----+------+| id | name |+----+------+| 1 | aa || 2 | bb || 3 | cc || 4 | ee |+----+------+4 rows in set (0.00 sec)
>>select *from test_myisam; +----+------+| id | name |+----+------+| 1 | aa || 2 | bb || 3 | cc || 6 | ee |+----+------+4 rows in set (0.00 sec) 

小结:对于MyISAM和InnoDB的表,因为存储引擎对于自增列的实现机制不同,ID值也可能会有所不同,对于InnoDB存储引擎的表,ID是按照max(id)+1的算法来计算的。

但是这个答案对吗?显然不是,因为还是不够严谨,我们进入第三轮反转。

这个问题不够严谨是因为技术是逐步发展的,这个问题在MySQL 8.0中有了答案,对于InnoDB的自增列信息,如果断电之后会直接丢失,很可能造成级联从库间的数据同步出现问题,而在MySQL 8.0之后,这个信息写入了共享表空间中,所以服务重启之后,还是可以继续追溯这个自增列的ID变化情况的。

限于篇幅,因为测试日志是很相似的,我就直接给出测试后的日志,这是在数据库重启之后的自增列情况,可以看到test_innodb和test_myisam的自增列是完全一样的。

mysql> show create table test_myisam\G***************************
1. row *************************** Table: test_myisamCreate Table:
CREATE TABLE `test_myisam`( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1
row in set (0.00 sec)
mysql> show create table test_innodb\G ***************************
1. row *************************** Table: test_innodbCreate Table: CREATE TABLE `test_innodb`
( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=
6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec) 

我们做一个小结:

在MySQL 8.0之前:

1)如果是MyISAM表,则数据库重启后,ID值为18

2)如果是InnoDB表,则数据库重启后,ID值为15

在MySQL 8.0开始,

1)如果是MyISAM表,则数据库重启后,ID值为18

2)如果是InnoDB表,则数据库重启后,ID值为18

此处需要补充的是,对于ID自增列,在MySQL 5.7中可以使用sys schema来进行有效监控了,可以查看视图schema_auto_increment_columns 来进行列值溢出的有效判断。

更难能可贵的是,如果是MySQL 5.7版本以下,虽然没有sys schema特性,但是可以复用MySQL 5.7中的schema_auto_increment_columns 的视图语句,也是可以对列值溢出进行有效判断的。

阅读目录(置顶)(长期更新计算机领域知识)https://blog.csdn.net/weixin_43392489/article/details/102380691

阅读目录(置顶)(长期更新计算机领域知识)https://blog.csdn.net/weixin_43392489/article/details/102380882

阅读目录(置顶)(长期科技领域知识)https://blog.csdn.net/weixin_43392489/article/details/102600114

第四十一期:一道经典的MySQL面试题,答案出现三次反转相关推荐

  1. mysql 表结构反转_一道经典的MySQL面试题,答案出现三次反转

    前几天偶然看到大家在讨论一道面试题,而且答案也不够统一,我感觉蛮有意思,在此就做一个解读,整个过程中确实会有几处反转. 我们先来看下题目: 一张表,里面有ID自增主键,当insert了17条记录之后, ...

  2. el-table固定列之后没有出现滚动条_一道经典的MySQL面试题,答案出现三次反转...

    前几天偶然看到大家在讨论一道面试题,而且答案也不够统一,我感觉蛮有意思,在此就做一个解读,整个过程中确实会有几处反转. 我们先来看下题目: 一张表,里面有ID自增主键,当insert了17条记录之后, ...

  3. 一道经典的JavaScript面试题

    一道经典的JavaScript面试题 转载于:https://www.cnblogs.com/suoking/p/5227430.html

  4. 一道经典的SQL面试题

    题:有一张表D,有a,b两列.a列表示用户b列表示地区.示例如下图:要求查出和用户为张三地区完全相同的其他用户 题解: 1.首先我们应该查出张三的地区,然后在查出在此范围内的用户,则有一下语句 SEL ...

  5. 阿里P8整理Mysql面试题答案,助你面试“脱颖而出”

    前言 附面试思维导图: 作为一名开发人员,每天英高都在和数据库进行着斗智斗勇,尤其是互联网行业,对MySQL的使用是比较多的.同样的,因为mysql的重要性以及普及性,在面试的时候一定是一个面试的重点 ...

  6. 【面试】ASP.NET Core+Redis+Mysql面试题答案

    .NET Core 1.如何在ASP.NET Core中激活Session功能   写的好啊,Inb哥,我是废物 2.什么是中间件   中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提 ...

  7. 阿里P8整理Mysql面试题答案,助你“脱颖而出”,吊打面试官!(建议收藏)

    前言 作为一名开发人员,每天英高都在和数据库进行着斗智斗勇,尤其是互联网行业,对MySQL的使用是比较多的.同样的,因为mysql的重要性以及普及性,在面试的时候一定是一个面试的重点或者说常问问题,说 ...

  8. MySQL面试题 数据库设计三范式

    第一范式 属性(字段)的原子性约束,要求属性具有原子性,不可再分割: 比如个人信息,个人信息不能作为一个字段,它可以再分为姓名.name.age等: 第二范式 记录的惟一性约束,要求记录有惟一标识,每 ...

  9. 一道十分经典的intern面试题(String字符串)让你彻底搞懂intern方法

    首先我觉得要看懂这道题还得知道inren()方法的一些知识,还要知道new String("ab")到底创建了几个对象. 一.intern()方法的理解(重难点) 1.intern ...

最新文章

  1. Java高并发编程(三):Java内存模型
  2. Visual Basic 终于要衰落了吗?
  3. 概率论——随机变量和的期望
  4. 小型企业网络规划与组建方案
  5. ImageJ的Macro语言实例教程
  6. 复旦版最佳医院排行 沪21家医院入选全国百佳
  7. 荣耀笔记本linux隐藏分区,删除品牌机和笔记本隐藏分区的方法
  8. 微信公众号二次开发可以做哪些功能?
  9. 通过css和js实现流星雨效果
  10. python3数据处理(一)-- 解析XML,Excle文件
  11. 论文笔记:Intention Oriented Image Captions with Guiding Objects
  12. python (ploit)
  13. SpringAop篇 (2) Spring中的切面编程技术 AspectJ
  14. 擎创科技正式加入openEuler社区,共建开源生态
  15. 麦子学院学习视频之机器学习(1):1.1 机器学习介绍
  16. php html钓鱼源码,PHP全新仿QQ空间钓鱼源码
  17. 吸引力法则,你变了,一切就变了
  18. E_可怜小凡_模拟_lv4
  19. 为什么spring cloud服务启动之后回到命令行会自动挂掉
  20. 【用AI打击犯罪】高质量实际监控视频异常检测(免费下载:抢劫,行窃,偷窃,射击,袭击,打架,纵火,爆炸,逮捕,车祸等)

热门文章

  1. UITableViewCell在非Nib及Cell重用下设置CellStyle
  2. 日志处理(二) 日志组件logback的介绍及配置使用方法(转)
  3. Android 开发环境在 Windows7 下的部署安装
  4. 全文服务(Microsoft 搜索)不可用。系统管理员必须启动此服务
  5. oracle 与 client端执行结果不一致_Oracle -PLSQLDeveloper 13 数据库连接
  6. monthdiff oracle_Oracle计算时间差函数
  7. 获得picker选项的当前年月值_如果你用OPPO手机!千万记得开启开发者选项,手机性能大幅度提升...
  8. 华为OV小米鸿蒙,华为鸿蒙开源,小米OV们会采用吗?
  9. 解决问题E: 无法获得锁 /var/lib/dpkg/lock - open (11: 资源暂时不可用) E: 无法锁定管理目录,
  10. Workbox.routing v3.x 中文版