从库上执行SHOW REPLICA STATUS\G,Seconds_Behind_Master从几十秒一路涨到几万,或者Replica_SQL_Running变成No、复制整个停住。这两种是MySQL主从里最高频的故障形态,排查路径完全不同,动手前先看清是哪条线程出的问题。
Replica_IO_Running与Replica_SQL_Running要分开看。前者是No,说明从库连不上主库或者拉不到binlog,问题出在网络、复制账号、主库的binlog文件;后者是No,说明binlog已经拉到本地中继日志了,是回放时出的错,问题在从库自己的数据或配置。两个都是Yes但延迟持续变大,那是回放跟不上,属于性能问题。
状态字段的名字跟版本绑定。MySQL 8.4.0、8.0.36与5.7.44,这三套版本里字段命名不一样:8.4.0里改成了Replica_IO_Running、Source_Log_File、Seconds_Behind_Source,8.0系列还保留Slave_IO_Running、Master_Log_File、Seconds_Behind_Master这一套老名字。照着文档抄命令对不上号的时候,先SELECT VERSION()确认一下,能省掉不少时间。
IO线程停了:1236
Last_IO_Error里出现Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file',意思很直接,从库报上去的位点对应的binlog文件在主库上找不着了。开了GTID之后措辞会变成the master has purged binary logs containing GTIDs the slave requires,说的是同一件事。
先去主库确认那个文件还在不在、保留期设的多少:
SHOW BINARY LOGS; SELECT @@binlog_expire_logs_seconds / 86400 AS keep_days, @@gtid_purged;
MySQL 8.0起用binlog_expire_logs_seconds替掉了expire_logs_days,默认值2592000秒。麻烦的是不少环境照着老教程把expire_logs_days设成了1或者3,从库一停就超出保留窗口。遇到过一次是备份脚本半夜在主库跑PURGE BINARY LOGS,把从库还没拉走的文件清掉了,从那以后清理动作改成确认所有从库位点之后才执行。
确认补不回来就别在参数上纠结,重做从库更快。导出时带上--single-transaction与--source-data=2(8.0.26之前叫--master-data=2),文件前几十行就写着坐标:
mysqldump -uroot -p --single-transaction --source-data=2 --all-databases | gzip > full.sql.gz zcat full.sql.gz | head -50 | grep -i "CHANGE MASTER"
库到了几百GB就别用mysqldump慢慢等,XtraBackup 8.0.35、8.4.0,物理备份恢复快得多,实测一个400GB的库从导出到追平,逻辑备份要六七个小时,物理备份一个半小时就完事。
SQL线程停了:1062与1032
Last_SQL_Error里看到Could not execute Write_rows event on table db.t_order; Duplicate entry '10241' for key 'PRIMARY', Error_code: 1062,说明从库上已经有这条主键,主库又插了一次。1032是反过来:要更新或者删除的行在从库上不存在。
这两个错基本指向同一件事,从库被写过了。可能是有人直接在从库上改数据,可能是定时任务连错了实例,也可能是切主之后旧主库没关干净又写了一批。先确认只读有没有开对:
SELECT @@read_only, @@super_read_only; SET GLOBAL super_read_only = ON;
read_only挡不住有SUPER权限的账号,得连super_read_only一起开,这两个参数MySQL 5.7.8之后才有,更早的版本只能靠权限收口。
数据怎么补看差多少。差几条就把冲突行手工改掉再START REPLICA;差得多就别硬修,pt-table-checksum校验、pt-table-sync修复,或者直接重做。想快可以用sql_slave_skip_counter跳过去,但跳过之后主从数据已经不一致了,后面报的错会更难判断。真的要跳,先把冲突那行的原始值记下来。
两个线程都在,延迟还是大
多半是回放速度跟不上写入。MySQL 8.0.27、8.0.30、8.0.36与8.4.0,并行复制的默认值一路在变:8.0.27之前replica_parallel_workers默认是0,等于单线程;8.0.27起改成4,8.0.30起再设成0会直接告警;到了8.4.0,binlog_transaction_dependency_tracking的默认从COMMIT_ORDER换成了WRITESET,能并行的事务更多。
调workers之前先查一遍表结构。没有主键的表在并行回放下会退化成全表扫描,见过一张900万行无主键的表,主库一次批量更新让延迟冲到二十多分钟。主键补上再谈并行度:
STOP REPLICA; SET GLOBAL replica_parallel_workers = 8; SET GLOBAL replica_preserve_commit_order = ON; START REPLICA;
workers不是越大越好。之前在一台16核的从库上从4调到16有明显效果,再往上调到32反而更慢,worker之间抢同一批行的锁。改完盯着Seconds_Behind_Master是不是稳步往回走,来回跳说明没解决问题。
大事务是另一个常见来源。主库一条DELETE干掉几百万行,binlog里就是一个巨大的event,从库回放得一条一条跑,期间延迟会一路涨。要提前发现,看主库当前有没有跑了很久的事务:
SELECT trx_id, trx_started, trx_rows_modified FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 5;
trx_rows_modified上百万、trx_started是几十分钟前的那种,就是要盯的对象。批量变更拆成小批执行,比事后调参数管用。
Seconds_Behind_Master本身也不太准
这个值是拿中继日志里event的时间戳跟当前时间算的差,IO线程断掉的时候它显示NULL,看着像没延迟,其实复制已经停了。大事务执行期间它会在0和一个很大的数之间跳。拿它做告警阈值,得配合Replica_IO_Running、Replica_SQL_Running一起判,单看延迟会被这两个特征骗到。更准的做法是pt-heartbeat在主库打时间戳表,从库算差值。
还有一个容易忽略的点,从库磁盘写满会报Error_code: 1594,Relay log write failure,报错长相跟复制逻辑完全无关,实际就是磁盘或者inode用完了,df -h与df -i都要看。
数据截至2026年10月,涉及的行为以MySQL 8.0.36、8.4.0,这两个版本为准。