看 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/<code>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抽样比对一次关键表。
