问题说明
在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值一起传递到从库,保证主从数据完全一致。