MySQL 主从延迟:从 Seconds_Behind_Master 说起

米阳 2023-3-18 4 3/18

读写分离之后最常见的抱怨是“刚提交的数据查不到”。多数情况就是主从延迟。

先确认是不是延迟

SHOW SLAVE STATUS\G

关注三行:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 12

前两个是 Yes 说明复制线程正常;第三个大于 0 就是延迟。但这个值不准——它比较的是 SQL 线程正在执行的事件时间戳和从库当前时间,如果主库长时间没有写入,它可能显示 0 但其实积压了很多 relay log。

更准的算法:在主库写一个时间戳,从库读出来做差。或者看 SHOW SLAVE STATUS 里的 Relay_Log_Space 是否持续增长。

常见原因

1. 大事务

一条 UPDATE 改了 50 万行,主库几秒钟跑完,从库的 SQL 线程要单线程回放这个事务,可能跑十几分钟。期间所有后面的操作都堵着。

对策:拆成小批量,每次 1000 行,循环处理。

2. 从库没有索引

主库有索引,从库因为某些原因少建了,回放时的 UPDATE/DELETE 变成全表扫描。这类问题很隐蔽,pt-table-checksum 能对比表结构。

3. 从库配置比主库差

给从库配了低配机器,或者混部了其他服务。从库的 innodb_flush_log_at_trx_commit 可以设成 2(牺牲一点安全性换吞吐),前提是能接受宕机丢少量数据。

4. 单线程回放

MySQL 5.7 支持并行复制,但默认参数保守:

slave_parallel_workers = 4
slave_parallel_type = LOGICAL_CLOCK
slave_preserve_commit_order = 1

LOGICAL_CLOCK 按组提交并行,效果比 DATABASE 模式好。preserve_commit_order 保证从库提交顺序和主库一致,避免读到中间状态。

业务上怎么兜

延迟不可能完全消除,业务层要能接受:

  • 写后立即读:强制走主库。可以在写操作后把用户 id 放进“最近写过”的集合,读请求检查这个集合
  • 最终一致:写完之后返回,前端轮询或用 WebSocket 推送结果
  • 容忍延迟的读:列表、统计这类走从库,用户感知不到

监控

写个脚本每分钟采集一次,超过 5 秒告警:

mysql -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master

但别只看这个值,配合 SHOW PROCESSLIST 看 SQL 线程正在执行什么语句,能直接定位到是哪个大事务卡住的。

我那次的元凶是一条没加条件的 UPDATE ... WHERE create_time > '2023-01-01',影响了 30 万行。加上 LIMIT 分批之后恢复正常。

- THE END -

米阳

3月18日15:10

最后修改:2023年3月18日
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论