MySQL主从同步在运行中可能因为主键冲突、表结构不一致或误操作等原因发生复制报错,导致从库SQL线程停止。对于开启了GTID模式的集群,每个事务都有全局唯一的事务ID,我们可以通过操作GTID相关的系统变量,让从库主动跳过某些已知错误的事务,而无需像传统模式那样去计算二进制日志文件和位置点。

一、GTID模式下的事务标识原理
GTID全称是Global Transaction Identifier,由服务器UUID加一个递增序列号组成,格式类似 3e11fa47-71ca-11e1-9e33-c80aa9429562:23。主库提交一个事务就会分配一个新的GTID,并且写入二进制日志。从库应用事务时会记录自己已经执行过的GTID集合,这个集合保存在系统变量 gtid_executed 中。
因为GTID具有全局唯一性,从库只要发现自己收到的某个GTID已经在 gtid_executed 里,就会直接忽略它。利用这一点,我们可以人为构造一个“空事务”并把错误事务的GTID标记为已执行,从而达到跳过的效果。这种方式比基于 binlog 文件和 position 的跳过更安全,不容易因为算错位点而丢数据或重复执行。
二、使用GTID set跳过单个错误事务
当从库报出类似 "Could not execute Write_rows event" 的错误时,我们首先要在从库执行 show slave statusG 查看错误信息中的 GTID 编号,例如 Retrieved_Gtid_Set 显示主库传过来的事务是 3e11fa47-71ca-11e1-9e33-c80aa9429562:100,而 Executed_Gtid_Set 停在 :99。
此时可以在从库会话中,把下一个要执行的事务设成这个出错的GTID,并提交一个空事务,命令如下:
-- 停止复制线程 STOP SLAVE; -- 设置即将执行的GTID为出错事务 SET SESSION gtid_next = '3e11fa47-71ca-11e1-9e33-c80aa9429562:100'; -- 提交一个空事务,将该GTID标记为已执行 BEGIN; COMMIT; -- 恢复gtid_next为自动分配 SET SESSION gtid_next = 'AUTOMATIC'; -- 重新启动复制 START SLAVE;
上面的代码里,gtid_next 告诉MySQL下一个事务就用指定的GTID,而空事务本身不修改任何数据,但会让该GTID进入 gtid_executed。从库随后就不会再尝试应用主库传来的同一个GTID,复制得以继续。
这种方法的优点是精准、影响范围小,只会跳过明确指定的那一个事务。缺点是如果错误事务本身包含重要数据修改,跳过会造成主从不一致,因此只建议用于可以确定无害的误操作或重复数据事务。
三、利用gtid_purged批量过滤事务集合
如果一批事务都出了问题,或者从库导入备份后需要忽略备份中自带的一部分GTID,可以通过 gtid_purged 来声明“这些GTID永远不需要执行”。gtid_purged 里的集合代表已经被清除或故意忽略的GTID,MySQL不会尝试去应用它们。
修改 gtid_purged 必须在 gtid_executed 为空且复制停止时进行,典型场景是从备份恢复后修正集合:
-- 停止复制 STOP SLAVE; -- 重置从库已执行信息(谨慎操作,仅用于新从库或恢复场景) RESET MASTER; -- 设置已清除的GTID集合,过滤掉不需要的事务 SET GLOBAL gtid_purged = '3e11fa47-71ca-11e1-9e33-c80aa9429562:100-105'; -- 启动复制 START SLAVE;
这里把 :100 到 :105 这六个事务放入 gtid_purged,从库会认为这些事务已经处理过或者无需处理,从而跳过它们后面的复制衔接。注意 gtid_purged 是全局变量且影响整个实例,不能只针对某个库,因此批量跳过前要确认这些事务对所有库都是可忽略的。
相比逐条注入空事务,gtid_purged 更适合在搭建从库或做数据修复时一次性划定范围。但在线上运行的主从关系中随意重置容易引发数据断裂,必须配合备份与业务暂停来操作。
四、跳过事务后的检查与风险
无论用哪种GTID set方式跳过,完成后都要通过 show slave statusG 观察 Slave_IO_Running 与 Slave_SQL_Running 是否都为 Yes,以及 Seconds_Behind_Master 是否在下降。同时对比主从相关表的数据行数,确认没有因为跳过造成明显偏差。
需要强调的是,GTID跳过只是让复制继续的手段,不是修复数据的手段。如果被跳过的事务修改了业务数据,从库就会永久缺少这部分变更。因此在核心业务表上,更推荐先人工补数据再跳过,或者使用 pt-table-checksum 这类工具定期校验主从一致性,把风险控制在可接受范围。
五、传统位点跳过与GTID跳过的对比
在没有GTID的旧版本里,我们只能用 SET GLOBAL sql_slave_skip_counter = 1 然后 START SLAVE 来跳过当前事件,但它按事件计数而不是事务,很容易在多事件事务里跳错位置。GTID方式以事务为单位,并且集合可见、可审计,明显更可靠。
| 方式 | 跳过单位 | 是否需要算位点 | 安全性 |
|---|---|---|---|
| sql_slave_skip_counter | 事件 | 是 | 较低 |
| GTID空事务 | 事务 | 否 | 较高 |
| gtid_purged集合 | 事务区间 | 否 | 中(影响全局) |
从表中可以看出,GTID相关方法在操作直观性和准确性上都有优势。只要理清事务边界,就可以用很小的代价恢复主从同步,而不必担心位点错乱引发二次故障。