定位主从复制中断需先查SHOW SLAVE STATUS中的Exec_Master_Log_Pos和Relay_Log_File,据此解析本地中继日志定位失败SQL;GTID模式下应通过GTID差集跳过事务,而非sql_slave_skip_counter。

查 SHOW SLAVE STATUS 里的 Exec_Master_Log_Pos 和 Relay_Log_File
主从复制中断时,SHOW SLAVE STATUS\G 是第一手信息源。重点不是看 Seconds_Behind_Master 是否为 NULL,而是盯住两个字段:Exec_Master_Log_Pos(已执行到主库 binlog 的位置)和 Relay_Log_File(当前正在读的中继日志文件名)。这两个值组合起来,能精确定位「卡在哪条 event」。
常见错误现象是 Slave_SQL_Running: No,同时 Seconds_Behind_Master: NULL,但 Slave_IO_Running: Yes —— 这说明 IO 线程还在拉日志,SQL 线程已停在某条语句上。
-
Relay_Log_File告诉你去哪找中继日志,比如relay-bin.000042 -
Exec_Master_Log_Pos是它在主库 binlog 中对应的位置,不是中继日志里的偏移量 - 真正要解析的是中继日志本身,不是主库 binlog —— 因为可能经过过滤、重写或 GTID 转换
用 mysqlbinlog 解析中继日志定位具体 SQL
拿到 Relay_Log_File 和 Exec_Master_Log_Pos 后,不能直接拿主库 binlog 查,得解析从库本地的中继日志。命令如下:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/relay-bin.000042 | grep -A 5 -B 5 "position.*[0-9]*"
但更准的方法是用 --start-position 跳到出错位置附近(注意:中继日志里没有直接的 position 字段映射,所以得先用 mysqlbinlog -v 看头部的 # at 行,找到最接近 Exec_Master_Log_Pos 的那个 event 起始点)。
- 中继日志默认不启用
log_slave_updates,所以它只存 SQL 线程要执行的内容,不含自身产生的日志 - 如果开启了 GTID,
Exec_Master_Log_Pos可能失效,优先看Retrieved_Gtid_Set和Executed_Gtid_Set的差集 -
mysqlbinlog -v输出里带### UPDATE或### INSERT的行才是实际执行的语句(基于行复制时)
跳过错误前先确认是不是主键冲突或重复插入
绝大多数复制中断来自唯一键冲突(1062 Duplicate entry)或外键约束失败(1557 Cannot add or update a child row),而不是语法错误。这类问题往往意味着主从数据已不一致,盲目跳过可能扩大偏差。
- 查错误日志:
tail -n 20 /var/log/mysql/error.log,搜索ERROR 1062或ERROR 1557 - 用
SELECT * FROM <table> WHERE <unique_col> = 'xxx'对比主从该记录是否存在、内容是否一致 -
SET GLOBAL sql_slave_skip_counter = 1仅适用于 STATEMENT 模式;ROW 模式下必须用gtid_next或临时禁用唯一检查(不推荐) - MySQL 8.0+ 支持
STOP REPLICA UNTIL SQL_AFTER_GTIDS,比跳计数器更安全
GTID 模式下别依赖 sql_slave_skip_counter
开启 GTID 后,sql_slave_skip_counter 被禁用,报错 ERROR 1794 (HY000): The replication repository cannot be modified when GTID mode is enabled。此时必须用 GTID 方式跳过:
SET @@SESSION.GTID_NEXT='f5b6d7a8-1234-11ee-a8a8-00155d012345:12345';<br>BEGIN; COMMIT;<br>SET @@SESSION.GTID_NEXT='AUTOMATIC';<br>START REPLICA;
其中 f5b6d7a8-1234-11ee-a8a8-00155d012345:12345 是你要跳过的那个 GTID,可从 SHOW SLAVE STATUS\G 的 Retrieved_Gtid_Set 和 Executed_Gtid_Set 差集中提取。
- 跳 GTID 前务必确认该事务在主库确实已成功执行,且从库跳过后不会导致逻辑不一致
- GTID 集合对比建议用 Python 或 shell 脚本做集合差,手动比对容易漏掉区间(如
a1-b2和a1-b1,b2-b2) - MySQL 5.7 开始支持
gtid_purged手动注入,但仅限全新从库初始化场景,线上慎用
真正难的不是定位那条 SQL,而是判断它为什么在从库执行失败——是主库写了脏数据?是应用双写绕过了主库?还是从库被误操作改了数据?查完日志后,多花两分钟核对主从表的 CHECKSUM TABLE 结果,比急着跳过更稳妥。


















