在一次宕机之后重启Mysql服务器并恢复数据的过程中发现了自增主键列的自增数值会回退,导致有数据上有冲突。经过一番的排查之后发现原来是主键自增值回退了,导致自增主键有重复导致外键关联失效引起的。
我们来举个例子说明一下具体的现象,例如,创建一个个InNoDB引擎表:
CREATE TABLE `bsession` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`aname` varchar(80) NOT NULL DEFAULT ‘’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
现在插入10条数据,再删除最后的几条,
INSERT INTO `bsession` (`aname`) values ('a'),('a'),('a'),('a'),('a'),('a'),('a'),('a'),('a'),('a’);
DELETE FROM `bsession` where `id` in (8,9,10);
删除操作之后,表中的数据仅有7条,最大的’id’ = 7。现在进行数据库重启,并重新插入10条数据。此时自增列是从8开始计数,还是从11开始计数呢?我想很多人都认为会从11开始计数,生成新纪录,但实际情况会超出我们的预期,下面我们实际验证一下:
你肯定会问了,这是为什么呢?
同样我也感觉很奇怪,于是就了解MySql的InnoDB引擎是如何处理自增列的:
原因是InnoDB引擎对AUTO_INCREMENT计数器是存放到主内存中的,并非硬盘。所以当重启后内存数据就丢失了!
我们来看官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html,中的【InnoDB AUTO_INCREMENT Counter Initialization】一节的详细说明:
不过这个特性将在Mysql的下一个版本8.0中更改,自增计数器每次更改时,当前的最大自增计数器值将会被写入redo log中,并保存到每个检查点的 InnoDB引擎的私有系统表中,实现自增计数器的持久化,重启后会保持一致。
当服器在Crash中的恢复重启过程中,InnoDB使用存储在系统字典表里的当前最大自增值初始化到内存,并且从最后一个检查点开始扫描Redo Log中写入的计数器值。如果Redo Log中的值大于内存中的计数器值,Redo Log中的值将会被采用。
关于后续版本中对于 自增列的处理机制 请查看官方文档的详细说明,这里不在赘述。https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html