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

我们先来看下题目:

一张表,里面有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存储引擎,来做同样的操作,看看重启后的差异情况。

>>createtabletest_innodb(idintprimarykeyauto_increment,namevarchar(30)) engine=innodb;>>createtabletest_myisam(idintprimarykeyauto_increment,namevarchar(30)) engine=myisam;

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

>>insertintotest_innodb(name)values('aa'),('bb'),('cc');Query OK, 3rowsaffected (0.00 sec)Records: 3 Duplicates: 0 Warnings: 0

>>insertintotest_myisam(name)values('aa'),('bb'),('cc'); Query OK, 3rowsaffected (0.00 sec)Records: 3 Duplicates: 0 Warnings: 0

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

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

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

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

>>insertintotest_innodb(id,name)values(5,'ee');Query OK, 1 row affected (0.00 sec)

>>insertintotest_myisam(id,name)values(5,'ee'); Query OK, 1 row affected (0.00 sec)

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

>>showcreatetabletest_innodb\GCREATETABLE`test_innodb` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=InnoDB AUTO_INCREMENT=6DEFAULTCHARSET=utf81 rowinset(0.00 sec)

删除id=5的记录

>>deletefromtest_innodbwhereid=5;Query OK, 1 row affected (0.01 sec)

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

>>showcreatetabletest_innodb\GCREATETABLE`test_innodb` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=InnoDB AUTO_INCREMENT=6DEFAULTCHARSET=utf81 rowinset(0.00 sec)

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

我们停止数据库

>>shutdown;Query OK, 0rowsaffected (0.00 sec)

重启数据库

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

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

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

>>showcreatetabletest_myisam\GCREATETABLE`test_myisam` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=MyISAM AUTO_INCREMENT=6DEFAULTCHARSET=utf81 rowinset(0.00 sec)

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

>>showcreatetabletest_innodb\G *************************** 1. row ***************************Table: test_innodbCreateTable:CREATETABLE`test_innodb` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=InnoDB AUTO_INCREMENT=4DEFAULTCHARSET=utf8

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

>>insertintotest_innodb(name)values('ee');Query OK, 1 row affected (0.00 sec)

>>insertintotest_myisam(name)values('ee'); Query OK, 1 row affected (0.00 sec)

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

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

>>select*fromtest_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> showcreatetabletest_myisam\G*************************** 1. row ***************************Table: test_myisamCreateTable:CREATETABLE`test_myisam` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=MyISAM AUTO_INCREMENT=6DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci1 rowinset(0.00 sec)

mysql> show createtabletest_innodb\G *************************** 1. row ***************************Table: test_innodbCreateTable:CREATETABLE`test_innodb` ( `id`int(11)NOTAUTO_INCREMENT, `name`varchar(30)DEFAULT,PRIMARYKEY(`id`)) ENGINE=InnoDB AUTO_INCREMENT=6DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci1 rowinset(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 的视图语句,也是可以对列值溢出进行有效判断的。

【编辑推荐】

【责任编辑:华轩 TEL:(010)68476606】

点赞 0

mysql 表结构反转_一道经典的MySQL面试题,答案出现三次反转相关推荐

  1. mysql 表结构监控_性能测试之mysql监控、优化

    共享表空间还有一个缺点就是不能自动收缩,自动收缩是什么意思呢,刚建表的时候,表里面数据很少,就1条数据,可能占用空间就几kb,到后来数据多了,占用了10个G的空间,然后发现有一些数据都是垃圾数据,删了 ...

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

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

  3. ogg mysql表结构不一致_求助:OGG双向同步,数据不一致如何解决?(已解决)

    本帖最后由 hbm1985 于 2012-5-10 07:39 编辑 本人在搭建oracle goldengate 双向同步时,遇到一个问题:当两边同时更新同一条记录时,会出现两边数据不一致的问题. ...

  4. mysql表名备注_「备注」mysql添加备注信息 - seo实验室

    备注 前言 这连天有人问我一些mysql的备注信息,不得已还是写一遍博客吧 1.创建表的时候,添加表名备注和字段备注 CREATE TABLE `healerjean_comment` ( `id` ...

  5. mysql修改工资字段_基于Linux的MySQL操作实例(修改表结构,MySQL索引,MySQL数据引擎)...

    基于Linux的MySQL操作实例(修改表结构,MySQL索引,MySQL数据引擎) 前言 本篇是基于Linux下针对MySQL表结构的修改,MySQL索引的操作以及MySQL数据引擎的配置和说明. ...

  6. nagios监控 mysql 表结构

    引言 为了给客户演示对mysql表结构的监控,在搜了很久之后发现不得不自己写一个脚本了.percona这么牛B的公司竟然没有提供一个这方面的工具,看来客户的要求有点花儿不实啊...这个问题一共花费了我 ...

  7. JAVA实现导出mysql表结构到Word详细注解版

    JAVA实现导出mysql表结构到Word详细注解版 转自https://blog.csdn.net/weixin_42041153/article/details/109739073 本文在原文中一 ...

  8. 修改mysql表结构语句

    昨天在执行碰到几个报错,总提示缺少mysql表结构里的字段什么的,当时有点没头绪不知道从哪里着手,再次记录一下修改表结构的语句,保证下次不会在出现此类问题; mysql 修改表结构语句 ALTER T ...

  9. mysql schema 同步_GitHub - naryn/mysql-schema-sync: mysql表结构自动同步工具

    mysql-schema-sync mysql表结构自动同步工具 用于将 线上 数据库结构变化同步到 本地环境! 支持功能: 同步新表 同步字段 变动:新增.修改 同步索引 变动:新增.修改 支持预览 ...

最新文章

  1. JAVA面试题系列:如何解决Redis的并发竞争问题
  2. 记录使用Spartan-6进行流水灯控制的实验
  3. 基础篇-verilog-按位与和逻辑与
  4. FloodFill算法详解及应用
  5. 2×3卡方检验prism_分类变量的相关性:五分钟掌握卡方检验「从理论到Python实战」...
  6. 高并发下Java多线程编程基础
  7. 蚂蚁金服 AntV F2 3.6 发布,更强!更快!
  8. 你的主机中的软件中止了一个已建立的连接。_winscp中文版,winscp中文版软件的使用技巧...
  9. Linux(CentOS7)下安装mysql
  10. Slava and tanks(CodeForces - 877C)
  11. Git问题解决:warning: Pulling without specifying how to reconcile divergent branches is discouraged. You
  12. Ubuntu下VSCode使用等宽字体设置
  13. 计算机详细配置快捷键,Win7系统电脑快捷键设置大全
  14. 你的人生,就是从这一刻开始毁掉的
  15. MySQL子查询的优缺点_浅谈mysql的子查询
  16. Cloudera Manager安装之Cloudera Manager安装前准备(CentOS6.5)(一)
  17. 汽车软件竞争进入“深水区”,诚迈科技如何抢滩?
  18. 贝壳找房面试之c++基础问答
  19. 关于python浮点数类型错误的是_关于Python的数字类型,以下选项中描述错误的是...
  20. oracle no privileges on tablespace 'USERS

热门文章

  1. 【clickhouse】clickhouse表引擎之CollapsingMergeTree
  2. 【Es】Es 集群设置分片很大导致集群无法选举主节点异常等
  3. 【ambari】Ambari Rest api 使用
  4. 【Docker】ERROR: Could not find a version that satisfies the requirement pytest==5.0.1
  5. 【Java】5 个刁钻的 String 面试题
  6. Java中的JsonConfig详解
  7. 60-300-024-使用-延迟数据-Flink中延时调用设计与实现
  8. 95-30-020-java.util-Map
  9. 【Flink】Flink 多并行度下的 watermark触发机制
  10. 【hive】hive权限