MySQL 主从延迟:从现象到根治

读写分离上线后最常见的线上问题:用户刚提交的数据,刷新页面就没了——主从延迟。这篇文章从延迟的成因讲起,到监控、优化、业务兜底,一套完整方案。

一、延迟从哪来

1 2 3 4 = . . . . b + i n b l i o n g l o g S Q L 线 S Q L 线

二、监控:看不到就管不了

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 文件号 + 位置)。

> 3 s > 3 0 s

三、优化:降延迟三板斧

1. 从库并行重放

1
2
3
# MySQL 5.7+:并行复制(按库/按事务组)
slave_parallel_workers = 8
slave_parallel_type = LOGICAL_CLOCK   # 事务组并行(推荐)

原理:同一事务组内无冲突的事务并行重放,延迟可降 80%+。

2. 消灭大事务

1 0 = D D L U + P + D A 1 b T 0 i p E 0 n t + 0 l - o o b g s i c n l o g

3. 索引对齐

" "

四、业务兜底:读不到就"读主"

延迟优化不到 0,业务要有兜底:

方案 1:关键读走主库

1
2
3
4
5
// 强一致场景(刚提交的数据必须立刻读到)走主库
func GetOrderAfterWrite(orderId string) *Order {
    // 写后立即读 → 主库(强一致)
    return queryMaster("SELECT * FROM orders WHERE id = ?", orderId)
}

原则“写后读"场景(订单详情、评论发布、资料修改)走主库,其他读走从库。

方案 2:延迟可接受标记

" " " " " > " - 2 s

方案 3:缓存先写

+ = /

五、踩坑记录

  1. 从库删索引"优化”:重放变全表扫,延迟暴涨 → 从库结构必须和主库一致
  2. 监控只看 Seconds_Behind_Master:从库空闲时虚报 0 → 用位点差 + 心跳表双监控
  3. 延迟时把流量切到从库:延迟 30s 还在从库读 → 业务直接"丢数据" → 延迟超过阈值切主库读
  4. 半同步的坑:半同步只保证"主库确认了从库收到 binlog",不保证"从库重放完"——半同步 ≠ 无延迟

六、什么时候用半同步

+ b i n l o g +

总结

主从延迟的核心认知:

  1. 延迟的根源:重放慢(大事务/缺索引)> 传输慢
  2. 监控用位点差:Seconds_Behind_Master 会虚报
  3. 优化三板斧:并行复制、拆大事务、索引对齐
  4. 业务兜底:写后读走主库、延迟阈值切换

主从延迟不可能为 0,但可以"业务感知不到":优化把延迟降到秒内,兜底把延迟关进笼子——读写分离才能稳定上线。