Skip to content

如何处理 MySQL 的主从同步延迟? ​

  • 标签:后端、MySQL、数据库

🏛 业务架构师 ​

主从延迟的处理我分应急、根治、规避三层,这也是线上真实处置顺序:

应急(分钟级动作)

  • 定位延迟来源:大事务/大 DDL 回放(Seconds_Behind_Master 飙升常见于批量 update)、从库单线程回放、从库机器差或被报表查询拖垮;
  • 临时切读流量回主库(要评估主库容量),或扩从库分摊读。

根治(按收益排)

  • 并行复制:WRITESET/LOGICAL_CLOCK 让从库多线程回放,对多数业务延迟下降一个量级——性价比最高的一步;
  • 消灭大事务:批量任务改小批量分次提交(binlog 单事务过大会卡住回放);DDL 用 gh-ost/pt-osc 平滑做;
  • 从库资源与隔离:从库配置对齐主库、报表/分析类查询隔离到专用从库——别让运营一条 select 拖垮所有业务读。

规避(业务设计层,最重要)

延迟是异步复制的天性,业务要围绕"写后立读"设计:

  • 金额、订单状态这类"用户马上要看"的关键读强制路由主库(注解标记 + AOP 数据源路由);
  • 写成功后短窗口内(如 2 秒)该用户的读走主库;
  • 或写后写缓存标记,读到标记走主库。

业务视角的总结:主从延迟问题的本质是**"一致性体验"的产品定义**——哪些场景允许旧数据、哪些必须新数据,和产品对齐后按场景路由,比无限追求"延迟为零"便宜得多也现实得多。

🏗 系统架构师 ​

主从延迟的本质:主库并行写、从库回放受限于复制链路与单线程/有限并行度,读写分离后"写后立即读"会读到旧数据。治理我分四层:

源头减负(最重要):

  • 大事务/大 DDL 是延迟主因,改造为分批提交(每批千行级);避免一次性 delete 百万行;
  • 从库承担的报表/分析查询抢资源,剥离到专门的延迟容忍实例或数仓。

复制提速:

  • 5.7+ 开并行复制:slave_parallel_type=LOGICAL_CLOCK + slave_parallel_workers,按组提交并行回放;8.0 writeset 进一步提升并行度,能扛住大部分延迟;
  • 半同步保证至少一个从库实时收到,减少故障切换丢数据。

读写路由策略:

  • 关键读走主库:写后 N 秒内(会话级标记 / cookie 时间戳)的读强制主库;
  • 中间件延迟感知路由:ProxySQL/ShardingSphere 检测 Seconds_Behind_Master,超阈值降级读主;
  • 会话粘性:GTID 等待(WAIT_FOR_EXECUTED_GTID_SET)确保读到本次写入。

兜底:监控 Seconds_Behind_Master 与 pt-heartbeat(更准的实测延迟),超阈值告警并自动摘除该从库。我的经验:并行复制 + 关键读走主,能解决 90% 的业务延迟问题。

💻 全栈开发专家 ​

先定位原因,再按场景选方案。延迟本质 = 从库消费 binlog 速度跟不上主库生产速度。

应急(线上在延迟时):

  1. 关键读强制走主库:支付后查订单这类"写完立即读"的场景,路由层加开关走 master(注意主库压力,限定少量接口);
  2. SHOW SLAVE STATUS 看 Seconds_Behind_Master 和 Slave_SQL_Running_State 判断是 IO 慢还是 SQL 慢;
  3. 网关/中间件的"读等待":ShardingSphere 或自研路由支持"读从库前校验 GTID/位点,不够就临时回主库"。

根因治理(我按处理次数排):

  • 大事务:从库单事务串行重放,拆批(一条 update 百万行改成每 500 行一批);
  • 开启并行复制:slave_parallel_workers=8(5.7 按库并行,8.0 WRITESET 按事务依赖并行,效果显著);
  • 从库硬件:binlog 落盘/relay log 用独立磁盘,从库配置不低于主库;
  • 压缩传输与带宽:跨机房复制带宽不足时 binlog_transaction_dependency_tracking + 半同步外的优化;
  • 削峰:把批量离线任务挪到从库低峰或数仓跑,别在主库跑重查询产生大 binlog。

监控:Seconds_Behind_Master 有坑(0 不代表无延迟),我用 pt-heartbeat 打心跳表测真实延迟,超 1s 告警。