主从延迟需先确认是否真延迟:Seconds_Behind_Master仅在SQL和IO线程空闲时准确,为NULL通常表示SQL线程已停止;应检查SHOW SLAVE STATUS中两个Running状态,并比对主从位点定位真实延迟。
很多情况下所谓“延迟严重”,其实是监控误报或误读 Seconds_Behind_Master。这个值在从库 SQL 线程空闲、IO 线程也空闲时才准确;一旦从库正在执行大事务、或主库写入突增,它可能长时间卡在某个值不动,甚至为 NULL(比如 SQL 线程已停止)。
SHOW SLAVE STATUS\G,重点看 Slave_IO_Running 和 Slave_SQL_Running 是否都为 Yes
Seconds_Behind_Master 是 NULL,大概率是 SQL 线程挂了,不是慢,是停了SHOW MASTER STATUS 的 File/Position 和从库的 Master_Log_File/Read_Master_Log_Pos(IO 进度)以及 Relay_Master_Log_File/Exe
c_Master_Log_Pos(SQL 执行到哪),能更准判断是否真落后真正拖慢同步的,90% 是 SQL 线程执行慢,而不是网络或磁盘 IO。MySQL 5.7+ 可以用 performance_schema 查正在执行的复制事件:
SELECT * FROM performance_schema.replication_applier_status_by_worker\G
重点关注 LAST_SEEN_TRANSACTION 和 LAST_ERROR_NUMBER。常见卡点:
Lock wait timeout exceeded:从库有长事务或未提交事务锁表,阻塞了 SQL 线程回放Duplicate entry:主从数据不一致导致唯一键冲突,SQL 线程直接 stopUPDATE 影响 500 万行,从库单线程重放,耗时数小时binlog_format = STATEMENT 但用了非确定函数(如 NOW()、UUID()),导致日志无法安全重放开启并行复制(slave_parallel_workers > 0)确实有用,但前提是主库写入本身能按库/表拆分。如果所有写都在一个库一个表,开再多 worker 也没用——全串行。
binlog_format 是 ROW(必须,STATEMENT 模式下并行复制基本无效)slave_parallel_type = LOGICAL_CLOCK(比 DATABASE 更细粒度)slave_parallel_workers(建议设为 CPU 核数 -1,不要盲目设 64)SET GLOBAL sql_slave_skip_counter = 1; 或用 gtid_next 跳过指定事务UPDATE ... LIMIT 10000 + SLEEP(0.1))很多人查完 SQL 线程、开了并行,延迟还是下不去——这时候要看 I/O。
relay_log 文件默认写在 MySQL 数据目录,如果和 datadir 共用同一块慢盘(尤其是机械盘),relay log 写入 + 回放读取会互相抢占relay_log_space_limit:设得太小会导致频繁轮转 relay log,触发 fsync,拖慢 SQL 线程sync_relay_log = 1 是安全的,但每写一次 relay log 就刷盘一次;生产环境可考虑设为 10000(每 10000 次写刷一次),代价是 crash 后最多丢 10000 条 relay eventinnodb_flush_log_at_trx_commit = 1(主库也是),否则主从数据一致性无法保证,加速反而埋雷真正卡住的时候,往往不是配置没开,而是磁盘扛不住、事务太大、或者从库上跑了备份脚本把 IO 打满了。先看 iostat -x 1,再看 SHOW PROCESSLIST,比改参数管用得多。