MySQL 主从延迟排查:Seconds_Behind_Master 为什么不可全信
一、现象:Seconds_Behind_Master 很乐观,业务却在读旧数据
上周一个报表库的从库告警:主从延迟显示 0 秒,但业务侧偶尔读到几分钟前的数据。登录从库执行 SHOW SLAVE STATUS\G,Seconds_Behind_Master 确实是 0,Slave_SQL_Running 也是 Yes。问题在于这个指标衡量的是"SQL 线程当前正在执行的这条 event 的时间戳与 IO 线程收到的最新 event 时间戳之差",一旦 SQL 线程空闲,它就归零,并不能说明已经追平。
更可靠的判断是看 GTID 或 binlog 位点是否一致:
-- 主库
SHOW MASTER STATUS;
-- 从库
SHOW SLAVE STATUS\G -- 关注 Exec_Master_Log_Pos / Retrieved_Gtid_Set / Executed_Gtid_Set
用 pt-heartbeat 往主库写心跳表,再在从库读时间差,得到的是真实延迟。生产上我一直建议用心跳法,Seconds_Behind_Master 只作为辅助参考。
二、三类常见成因
1)大事务
一次 DELETE 删 200 万行、一个没加条件的 UPDATE,都会在从库上串行重放很久。主库上可能几十秒就结束,但从库的 SQL 线程要完整执行一遍。排查方法是在主库开 performance_schema 看长事务,或直接看从库正在执行的 event:
SHOW PROCESSLIST; -- 从库上 System user 的那条就是 SQL 线程
2)从库硬件或参数不匹配
主库用 NVMe、从库用普通云盘,或者从库 innodb_flush_log_at_trx_commit=1 而主库是 2,重放速度会差出数倍。另外 sync_binlog、innodb_io_capacity 也应按从库磁盘能力调整。
3)并行复制没开对
MySQL 5.7+ 的 slave_parallel_type=LOGICAL_CLOCK 配合 slave_parallel_workers=8 能显著提升重放速度,但前提是主库的组提交能形成足够的并行度。如果主库写入是单线程串行的,从库开再多 worker 也没用。
三、一次真实的排查过程
这次的问题最终定位到从库磁盘 IO 打满。iostat -x 1 显示 %util 长期 100%,await 超过 200ms。进一步查发现是备份任务和从库重放在抢 IO:每天凌晨的物理备份用 xbstream 压缩写本地盘,正好和业务高峰的 binlog 重放撞上。
处理办法有三步:
- 备份改到业务低峰,并且直接流式传到对象存储,不落本地盘;
- 从库
innodb_io_capacity从默认 200 调到 2000,匹配 SSD 能力; - 开启并行复制,
slave_parallel_workers=8。
调整后 pt-heartbeat 测得的延迟从峰值 300 秒降到 2 秒以内。
四、监控该看什么
- 用心跳表算真实延迟,阈值按业务能接受的脏读窗口设定,比如 5 秒;
- 监控
Slave_SQL_Running_State,出现Waiting for dependent transaction to commit说明并行复制在等依赖; - 监控从库磁盘
await和%util,IO 是延迟最常见的根因; - 监控
Relay_Log_Space增长趋势,持续增长说明 SQL 线程追不上。
五、小结
Seconds_Behind_Master 只能说明"现在有没有在干活",不能说明"是否已经追平"。做主从延迟治理,先用心跳法拿到真实数字,再从大事务、硬件参数、并行复制三个方向排查,最后一定要把备份、报表这类离线任务和重放 IO 隔离开。很多"玄学延迟"其实是资源争抢,而不是 MySQL 本身的问题。
- 点赞
- 收藏
- 关注作者
评论(0)