errant transaction指从库手动执行写操作并记录到binlog中产生的主库不存在的GTID,它破坏GTID序列完整性,导致故障切换时同步中断;需通过比对主从gtid_executed及replication_applier_status_by_worker定位源头。

errant transaction 是什么
errant transaction 指在从库上**手动执行了写操作**(比如 INSERT、UPDATE、DELETE),且该事务被记录进了从库的 binlog(在 log_bin=ON 且未设 sql_log_bin=0 的前提下),最终生成了一个主库没有的 GTID。它不是主库发来的,却混进了从库的 GTID 执行序列里。
为什么开启 GTID 后更容易暴露这个问题
GTID 复制要求每个事务有全局唯一标识,且从库必须按 GTID 顺序严格应用——不能跳过、不能重复、不能缺失。一旦从库自己写了数据,它的 Executed_Gtid_Set 就会包含一个主库 gtid_executed 里没有的 GTID。后续做故障切换(比如 MHA 或手动提升从库为主库)时,新主库会把这条“脏”GTID 发给其他从库,导致它们报错:Could not execute the transaction because it is not in the executed set 或直接拒绝同步。
- 主库重启后,其他从库尝试从新主库拉 binlog,发现某 GTID 已存在但没对应事件,直接中断
- 用
mysqlbinlog查看从库 binlog,能看到非主库 origin 的 GTID(如aaaa-bbbb-cccc:12345,但主库的 UUID 根本不是aaaa-bbbb-cccc) -
SHOW SLAVE STATUS\G中Retrieved_Gtid_Set和Executed_Gtid_Set出现不连续空缺
常见误操作场景
这些操作看似“救急”,实则埋雷:
- 在从库执行
INSERT INTO user VALUES (...)修复丢失用户,没关sql_log_bin - 运维用脚本批量清理从库日志表,脚本没加
SET sql_log_bin = 0 - 开发连错库,在从库跑 DML 迁移脚本,事后才发现
- 主库宕机期间,临时把从库切读写,恢复后没清空本地写入的 GTID 就重新挂回复制链
怎么确认是否存在 errant transaction
登录疑似有问题的从库,运行:
SELECT * FROM performance_schema.replication_applier_status_by_worker\G
再对比主从 GTID 集合:
SELECT @@global.gtid_executed;
如果从库返回的 GTID 集合里,出现一段形如 deadbeef-1234-5678-90ab-cdef12345678:100 的区间,而主库的 gtid_executed 完全不含这个 UUID 前缀,基本就是 errant。
真正麻烦的是:**你无法靠单个从库判断它是不是源头**——可能它只是中转了上游从库的 errant GTID。必须逐级向上比对,直到找到第一个多出该 GTID 的节点。


















