排查MySQL主从复制故障须先执行SHOW SLAVE STATUS\G,紧盯Slave_IO_Running和Slave_SQL_Running状态:No则分别定位网络/权限或数据冲突/日志损坏问题,并立即查看Last_IO_Error或Last_SQL_Error获取错误码与语句。

看 SHOW SLAVE STATUS\G 先锁定是 IO 还是 SQL 线程挂了
这是所有排查的起点,不是可选动作。登录从库执行这条命令后,只盯两行:Slave_IO_Running 和 Slave_SQL_Running。如果其中一个是 No,就立刻分清战场:
-
Slave_IO_Running: No→ 问题出在“拿不到日志”:主库连不上、binlog 文件名/位置错、权限不对、网络被拦(比如防火墙关了 3306 或 SELinux 没关) -
Slave_SQL_Running: No→ 问题出在“执行不了日志”:数据冲突(1062)、记录找不到(1032)、外键不满足(1452)、中继日志损坏(1594)
再顺手扫一眼 Last_IO_Error 或 Last_SQL_Error 字段——它直接告诉你错误码和原始语句,比翻日志快十倍。别跳过这步,很多人卡在反复重启却没看这一行。
查错误日志前先确认 log_error 路径
MySQL 错误日志不总在 /var/log/mysqld.log,尤其容器或自定义部署环境。先执行:mysql> SHOW VARIABLES LIKE 'log_error';
拿到真实路径后再 tail -n 50 /path/to/error.log。重点找这些关键词:
-
Connection refused、timeout、Can't connect to master→ 网络或主库 mysqld 没监听外网(检查bind-address是否为0.0.0.0) -
Table doesn't exist、Unknown database→ 从库缺库/表,不是数据不一致,是结构根本没同步过去 -
Relay log read failure、Event crc check failed→ 中继日志损坏,不能硬跳过,得开relay_log_recovery=1并重启 MySQL -
disk full、No space left on device→ 磁盘满了,relay log 写不进,清理空间比修复制更紧急
用 mysqlbinlog 反查出错那条 SQL
当 Last_SQL_Error 提到 “Duplicate entry '1001' for key 'PRIMARY'”,光删从库数据不够,得知道主库当时执行的是哪条 INSERT。方法是:
- 从
SHOW SLAVE STATUS\G中抄出Relay_Master_Log_File和Exec_Master_Log_Pos - 去主库执行:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/(把文件名和位点替换成实际值)mysql-bin.000006| grep -A 5 -B 5 "end_log_pos 12345"
输出里会看到完整 SQL 和涉及的库表字段。注意:MySQL 5.7+ 默认 binlog_format=ROW,所以看到的是 ### UPDATE ... ### WHERE 这类解析后内容,不是原始语句——但这反而更准,能看清具体哪一行被改。
跳过错误要分场景,sql_slave_skip_counter 不是万能膏药
这个变量只能跳过当前事件(一条语句),但有严格前提:
- 仅适用于
STATEMENT或混合模式;ROW格式下跳过可能跳掉多行更新,导致数据不一致 - 必须停掉复制:
STOP SLAVE;,再设:SET GLOBAL sql_slave_skip_counter = 1;,再START SLAVE; - 遇到
1590(LOST_EVENTS)、1594(relay log 损坏)这类系统级错误,跳过无效,必须重建 relay log 或重配主从 - GTID 模式下完全禁用
sql_slave_skip_counter,得用SET GTID_NEXT+ 空事务方式绕过
真正危险的不是报错本身,而是跳过之后没人验证从库数据是否还跟主库对得上。线上操作前,至少用 pt-table-checksum 抽样比对一次关键表。










