MySQL 主从延迟:从现象到根治#
读写分离上线后最常见的线上问题:用户刚提交的数据,刷新页面就没了——主从延迟。这篇文章从延迟的成因讲起,到监控、优化、业务兜底,一套完整方案。
一、延迟从哪来#
二、监控:看不到就管不了#
1
2
3
4
5
6
7
|
-- 从库执行,看延迟秒数
SHOW SLAVE STATUS\G
-- Seconds_Behind_Master:从库落后主库的秒数(关键指标)
-- 更精确:对比主从位点
SHOW MASTER STATUS; -- 主库 File+Position
SHOW SLAVE STATUS; -- 从库 Read_Master_Log_Pos
|
注意:Seconds_Behind_Master 有局限(从库空闲时可能显示 0,实际位点差很大)。生产用"位点差"监控(binlog 文件号 + 位置)。
三、优化:降延迟三板斧#
1. 从库并行重放#
1
2
3
|
# MySQL 5.7+:并行复制(按库/按事务组)
slave_parallel_workers = 8
slave_parallel_type = LOGICAL_CLOCK # 事务组并行(推荐)
|
原理:同一事务组内无冲突的事务并行重放,延迟可降 80%+。
2. 消灭大事务#
3. 索引对齐#
四、业务兜底:读不到就"读主"#
延迟优化不到 0,业务要有兜底:
方案 1:关键读走主库#
1
2
3
4
5
|
// 强一致场景(刚提交的数据必须立刻读到)走主库
func GetOrderAfterWrite(orderId string) *Order {
// 写后立即读 → 主库(强一致)
return queryMaster("SELECT * FROM orders WHERE id = ?", orderId)
}
|
原则:“写后读"场景(订单详情、评论发布、资料修改)走主库,其他读走从库。
方案 2:延迟可接受标记#
方案 3:缓存先写#
五、踩坑记录#
- 从库删索引"优化”:重放变全表扫,延迟暴涨 → 从库结构必须和主库一致
- 监控只看 Seconds_Behind_Master:从库空闲时虚报 0 → 用位点差 + 心跳表双监控
- 延迟时把流量切到从库:延迟 30s 还在从库读 → 业务直接"丢数据" → 延迟超过阈值切主库读
- 半同步的坑:半同步只保证"主库确认了从库收到 binlog",不保证"从库重放完"——半同步 ≠ 无延迟
六、什么时候用半同步#
主从延迟的核心认知:
- 延迟的根源:重放慢(大事务/缺索引)> 传输慢
- 监控用位点差:Seconds_Behind_Master 会虚报
- 优化三板斧:并行复制、拆大事务、索引对齐
- 业务兜底:写后读走主库、延迟阈值切换
主从延迟不可能为 0,但可以"业务感知不到":优化把延迟降到秒内,兜底把延迟关进笼子——读写分离才能稳定上线。
微信公众号「福清而不淡」
后端架构 · AI 工程 · 云原生的一线实践,扫码关注,不错过更新。
本文已同步发布到公众号,欢迎留言交流。