必须先STOP SLAVE SQL_THREAD再查@@global.gtid_executed,否则值瞬时不可比;需过滤ANONYMOUS事务并用WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS()验证执行一致性。

直接查 @@global.gtid_executed,但必须停 SQL 线程再查
主从切换后最常犯的错,就是没停 SQL_THREAD 就去查 gtid_executed。这时从库还在追日志,输出值是瞬时的、不可比对的。必须先执行 STOP SLAVE SQL_THREAD;,再查两边的 SELECT @@global.gtid_executed;。注意:不是 STOP SLAVE;(它会同时停 IO 线程,导致后续无法拉取新事件),只停 SQL 线程即可保留 relay log 可追溯性。
对比时过滤掉 ANONYMOUS 事务,否则误判“不一致”
gtid_executed 输出里可能混着 ANONYMOUS 类型事务(比如旧主库上用 SET GTID_NEXT='ANONYMOUS' 手动注入过),这类事务没有 UUID,无法参与 GTID 对齐逻辑,也不该计入一致性判断。检查方法:
SELECT * FROM mysql.gtid_executed WHERE source_uuid != 'ANONYMOUS';
更稳妥的做法是用正则提取非匿名段:SELECT SUBSTRING_INDEX(@@global.gtid_executed, 'ANONYMOUS', 1);,再和主库对应结果比对。若主库压根没 ANONYMOUS 事务,而从库有,说明复制链路曾被人工干预过,需回溯操作记录。
用 WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS() 等待追平,别信 Seconds_Behind_Master = 0
Seconds_Behind_Master = 0 只表示 relay log 已读完,不代表已执行完——SQL 线程可能卡在锁等待或大事务回放中。真正可靠的等待方式是调用内置函数:
- 先在主库记下当前 GTID 集:
SELECT @@global.gtid_executed;,假设输出为aaa-bbb-ccc:1-12345 - 在从库执行:
SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('aaa-bbb-ccc:1-12345');(MySQL 5.7.6+) - 该函数返回 0 表示已全部执行;返回 NULL 表示超时(默认 3600 秒),需检查是否被阻塞
这个函数比轮询 SHOW SLAVE STATUS\G 更准,且能暴露真实执行延迟而非网络延迟。
检查 mysql.gtid_executed 表是否有跨 UUID 断点
即使 gtid_executed 字符串看起来一致,底层表可能藏着“空洞”。执行:SELECT * FROM mysql.gtid_executed ORDER BY source_uuid, interval_start;,重点看每个 source_uuid 下的 interval_start 和 interval_end 是否连续。例如某行是 xxx:100-199,下一行却是 xxx:201-299,中间缺了 200,说明事务 200 丢失过——这种不一致不会触发复制错误,但数据已残缺。这种情况常见于 relay log 损坏后未启用 relay_log_recovery=1,或人为跳过事务后未清理残留状态。


















