生死看淡,不服就干!

使用ALTER TABLE新增自增列导致主从差异

Posted on By 笑东风

问题说明

在MySQL中,如果表当前没有定义主键,可以直接使用ALTER TABLE TB001 ADD ID INT AUTO_INCREMENT PRIMARY KEY来为TB001新增一个自增主键列, 但在复制场景下,主库执行的DDL操作会按照DDL语句复制到从库执行,只能保证主从节点之间的表结构一致,但无法保证相同记录在主库和从库上获得相同的自增ID,因此会存在数据不一致问题。

对于Innodb表,如果在表创建时没有指定主键,MySQL会隐式为Innodb表添加一个自增列,该内部自增列在复制时不会被复制到从库,因此导致相同数据在主从节点上拥有不同的自增ID值。在将非主键表改为自增主键表过程中,会按照原表数据存放位置顺序地拷贝到临时表中并生成自增ID值,最终导致主从数据不一致。

测试场景

在主库上执行下面SQL:


会话1
START TRANSACTION;
INSERT INTO TB001(C1) VALUES(1);

会话2:
START TRANSACTION;
INSERT INTO TB001(C1) VALUES(2);
COMMIT;

会话1:
COMMIT;

在主库上,由于会话1的INSERT比会话2的INSERT要早,因此会话1插入数据获得的内部自增ID要比会话2插入数据获得的ID要小。

在从库上,由于会话2的事务先于会话1的会话提交,会话2生成的binlog会先于会话1生成的binlog执行,因此会话1插入数据获得的内部自增ID要比会话2插入数据获得的ID要大。

由于上面的操作导致的数据差异,在执行ALTER TABLE操作后,“相同数据”获得不同的自增ID,导致复制异常。

解决办法

新建一个相同表结构的临时表,修改临时表增加自增列,将原表数据复制插入到临时表中,然后交换表名。

对于主库上临时表中插入的每条数据,会包含自增ID值一起传递到从库,保证主从数据完全一致。