MySQL主从同步跳过事务必须用SET GTID_NEXT配合BEGIN;COMMIT;伪造空事务,不可省略或仅用START TRANSACTION;GTID值须严格匹配原事务;跳过会导致GTID集合出现空洞,影响切主和延迟监控,且sql_slave_skip_counter在GTID模式下被禁用。

MySQL主从同步跳过事务时,SET GTID_NEXT 必须配合 BEGIN; COMMIT;
直接执行 SET GTID_NEXT = 'xxx-xxx-xxx:123'; 后不做事务操作,会导致后续所有事务被拒绝写入,报错 ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty. —— 这是因为 MySQL 认为你要手动注入一个 GTID,但没提供对应事务体,状态不一致。
正确做法是强制“伪造”一个空事务:
SET GTID_NEXT = 'e10c57b9-1234-11ef-8a5d-00155d012345:12345'; BEGIN; COMMIT; SET GTID_NEXT = 'AUTOMATIC';
- 必须用
BEGIN; COMMIT;,不能只用START TRANSACTION;或省略 -
GTID_NEXT值必须严格匹配原故障事务的完整 GTID(含 UUID 和事务号),大小写、连字符都不能错 - 执行前确认从库处于
STOP SLAVE;状态,否则可能被并行复制线程干扰
用 gtid_purged 跳过一批事务,要先清空 gtid_executed
想跳过从 e10c57b9-1234-11ef-8a5d-00155d012345:1000 到 :2000 的全部事务?不能直接改 gtid_purged。MySQL 要求新设的 gtid_purged 必须是当前 gtid_executed 的子集,而跳过意味着“宣称已执行但其实没执行”,逻辑冲突。
唯一可行路径是重置从库 GTID 状态:
STOP SLAVE; RESET MASTER; SET GLOBAL gtid_purged = 'e10c57b9-1234-11ef-8a5d-00155d012345:1-999,e10c57b9-1234-11ef-8a5d-00155d012345:2001-999999';
-
RESET MASTER会清空gtid_executed,这是设置gtid_purged的前提 - 拼接后的
gtid_purged必须覆盖所有你“假装已执行”的区间,漏掉一个就会在拉取 binlog 时卡住 - 该操作等价于丢弃当前从库全部复制位点,仅适用于你明确知道主库缺失部分不影响业务一致性
跳过事务后,SHOW SLAVE STATUS\G 的 Retrieved_Gtid_Set 和 Executed_Gtid_Set 不再连续
跳过之后你会发现两个集合中间出现“空洞”,比如 Executed_Gtid_Set 是 e10c...:1-999,e10c...:2001-2005,而主库实际已推进到 :3000。这本身不是错误,但会带来两个实际影响:
- 如果中途切主,新主库若未包含这些“跳过的 GTID”,从库无法自动对齐,
CHANGE MASTER TO ... GTID_SET会失败 - 监控脚本若依赖
Executed_Gtid_Set最大事务号做延迟估算,结果将严重失真 - Percona Toolkit 的
pt-heartbeat在 GTID 模式下不感知跳过行为,仍按顺序号算延迟,数值不可信
不要用 sql_slave_skip_counter 配合 GTID
在 GTID 模式下执行 SET GLOBAL sql_slave_skip_counter = 1; 会直接报错:ERROR 1776 (HY000): Cannot use sql_slave_skip_counter when @@GLOBAL.GTID_MODE = ON. 这不是配置问题,是 MySQL 强制限制——GTID 的核心设计就是靠全局唯一标识追踪事务,跳过计数器会破坏 GTID 链完整性。
如果你看到文档或老脚本还在混用,说明它根本没跑在 GTID 模式下,或者作者没验证过。
真正麻烦的地方在于:跳过动作本身不会留下日志痕迹,DBA 很难回溯“为什么这里断了一段 GTID”。线上做之前,务必用 mysqlbinlog 解析对应 binlog 文件,确认目标事务确实只含 DML 且无跨库依赖,否则跳完才发现主从数据不一致,就得全量重建从库。


















