CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `msg` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


insert into t (age, msg) values (1,'aaa'),(2,'bbb'),(3,'ccc');

id 为自增主键、age 为 唯一键

Mysql replace into-LMLPHP

尝试在主数据库执行 replace into

replace into t (age, msg) values (1, '111');

因为 age 为 1 已经存在了

Mysql replace into-LMLPHP

所以这里会先执行 delete 然后再执行 insert 、那么则会导致 AUTO_INCREMENT=5 了

但是对于 binlog 来说、仅仅是产生了一条 update 语句。

Mysql replace into-LMLPHP

这样子的话、会导致从数据库那边的 AUTO_INCREMENT 还是 4

Mysql replace into-LMLPHP

主从切换、新主插入失败

我们现在直接看下最新的数据

Mysql replace into-LMLPHP

Mysql replace into-LMLPHP

我们可以看到主数据现在的 AUTO_INCREMENT 的值为 18 、是符合预期的

我们再看看从数据库

Mysql replace into-LMLPHP

可以看到从数据库现在的 AUTO_INCREMENT 为 13 、是比主数据看落后的

现在我们直接将主数据库杀掉、从数据库成为新主、然后尝试 replace into 或者 insert into (不设置 id 的值)

Mysql replace into-LMLPHP

那么会报错主键冲突、插入失败、AUTO_INCREMENT 变为 14

一直直到 AUTO_INCREMENT 的值为 18

如果在这个过程中、我们指定 id 的值、那么 AUTO_INCREMENT 就会变成 id + 1(这个跟正常情况是一样的)

小结一下

  • insert 成功 AUTO_INCREMENT 会取当前 insert 的 id 的值 +1
  • 正常情况下 insert 失败不会导致 AUTO_INCREMENT 变化
  • 新主 AUTO_INCREMENT 落后于表中最大值时、即使是失败也会导致 AUTO_INCREMENT+1

生产上试过主从切换后、replace into 一直插入失败 、最终的结果是 DBA 手动指定自增列的值、该值为当前表中的最大值、才将 AUTO_INCREMENT 的值回归到正常的值。因为新主当时 AUTO_INCREMENT 的值已经落后很多了、插入一直失败、业务受到影响。

当然重启新主也可以重置 AUTO_INCREMENT

replace into 这个 bug 在 Mysql 5.7 中存在、但是在 mysql 8.0 的时候已经被修复了

本文由mdnice多平台发布

11-29 11:12