在互联网应用流量不断增长的背景下,单台 MySQL 实例往往难以同时承载高并发的读请求和写请求。读写分离成为缓解数据库压力、提升系统吞吐量的常用手段。然而,读写分离引入主从复制后,主从延迟问题也随之而来。本文围绕读写分离的常见实现方案、主从延迟的成因及应对策略展开,适合作为面试复习或生产实践的参考。
一、为什么要做读写分离
大多数业务系统呈现“读多写少”的特征,例如电商商品详情、新闻资讯、社交动态等场景,读请求可能占 80% 以上。通过将读请求分发到多个从库,写请求集中在主库,可以获得以下收益:
- 提升整体查询吞吐量,从库可水平扩展;
- 降低主库负载,避免慢查询拖垮写入;
- 增强可用性,从库可作为故障切换的候选节点。
但读写分离并非没有代价,最典型的问题就是主从复制延迟导致的数据不一致。
二、读写分离的常见实现方案
1. 基于中间件代理
常见的中间件包括 MyCat、ShardingSphere-Proxy、ProxySQL、MaxScale 等。应用连接中间件,由中间件根据 SQL 语义或注解路由到主库或从库。
优点:对应用透明,支持多语言,集中管理路由规则。
缺点:中间件本身可能成为瓶颈,增加运维复杂度,故障排查链路变长。
2. 基于客户端 SDK
在应用层引入如 ShardingSphere-JDBC、TDDL 等组件,由 SDK 解析 SQL 并选择数据源。
优点:无额外网络跳转,性能较好,配置灵活。
缺点:与语言绑定,升级需应用配合,侵入性较强。
3. 基于 Spring 动态数据源
在 Java 生态中,常通过 AbstractRoutingDataSource 配合 AOP 和自定义注解实现。例如定义 @ReadOnly 注解,方法执行前切换到从库数据源,执行后恢复主库。
这种方式实现简单,适合中小规模系统,但需要手动处理事务传播、强制走主库等场景。
4. 基于 MySQL Router 或云服务
MySQL Router 可根据读写端口自动路由;云数据库如 RDS 通常提供读写分离地址,底层自动管理主从拓扑。
三、主从延迟的成因
主从复制延迟是指从库执行完主库传来的 binlog 事件的时间落后于主库当前时间。常见原因包括:
- 从库性能不足:从库硬件配置低于主库,或从库上存在大量慢查询,导致 SQL 线程回放缓慢。
- 大事务:主库一次写入大量数据,binlog 事件巨大,从库需要长时间执行。
- 单线程回放瓶颈:MySQL 5.6 之前从库 SQL 线程为单线程,5.7 后支持基于组提交的并行复制,但若事务本身无法并行,仍会延迟。
- 网络延迟或带宽不足:主从之间传输 binlog 慢。
- 锁等待:从库回放时遇到表锁或行锁冲突,阻塞后续事件。
- 主库写入压力突增:短时间大量写入,从库回放速度跟不上。
四、主从一致性延迟处理策略
1. 强制走主库
对于一致性要求高的读请求,例如用户刚完成下单后立即查看订单详情,可以强制路由到主库。实现方式包括:
- 在方法或代码块上标注
@Master; - 在 ThreadLocal 中设置强制主库标记;
- 在中间件中通过 SQL 注释
/*master*/指定。
这是最简单直接的方案,但会削弱读写分离的效果,需控制使用范围。
2. 判断主从延迟并动态路由
通过 SHOW SLAVE STATUS 获取 Seconds_Behind_Master,若延迟超过阈值,则读请求走主库。也可以使用 pt-heartbeat 工具,在主库定期更新心跳表,从库读取该表时间戳来计算更精确的延迟。
中间件如 ProxySQL 支持根据延迟阈值自动摘除延迟过高的从库。
3. 利用 GTID 或位点等待
MySQL 提供 WAIT_FOR_EXECUTED_GTID_SET 函数,应用可以在写入后获取主库的 GTID,然后在从库上等待该 GTID 执行完成再读取。类似地,MASTER_POS_WAIT 可等待指定 binlog 位点。
这种方式能实现“写后读一致性”,但等待时间不可控,需设置超时并降级到主库。
4. 会话一致性(Session Consistency)
在应用层记录用户最近一次写入的 GTID 或时间戳,后续该用户的读请求携带此信息。中间件或 SDK 判断从库是否已同步到该位点,未同步则路由到主库。这是兼顾一致性与负载的常用方案。
5. 半同步复制与 Group Replication
- 半同步复制:主库提交事务后,至少等待一个从库确认收到 binlog 才返回成功。可减少数据丢失风险,但不能完全消除延迟。
- MySQL Group Replication / MGR:基于 Paxos 协议,提供强一致或最终一致选项,适合对一致性要求高的场景,但部署和运维复杂度较高。
6. 业务层补偿与容忍
对于非核心业务,可以接受短暂不一致,例如商品浏览量、评论列表等。通过缓存、异步刷新、前端提示等方式降低用户感知。同时建立监控告警,当延迟超过阈值时通知运维介入。
五、面试常见追问
-
读写分离后,事务中的读会走从库吗?
不会。事务应整体走主库,否则可能因主从延迟导致事务内读到不一致数据。 -
如何解决主库宕机后 binlog 未同步到从库?
可结合半同步复制、MGR 或定期备份与 binlog 恢复,并做好故障切换演练。 -
并行复制能完全解决延迟吗?
不能。并行复制依赖事务组提交,若主库写入本身是串行的,从库也无法并行。此外,大事务、锁冲突仍会导致延迟。 -
如何监控主从延迟?
常用Seconds_Behind_Master、pt-heartbeat、GTID 差值、Prometheus + mysqld_exporter 等。
六、总结
读写分离是提升 MySQL 读吞吐量的有效手段,但主从延迟是绕不开的问题。实际项目中,应根据业务一致性要求,组合使用强制走主库、延迟感知路由、GTID 等待、半同步复制等策略。没有一种方案能解决所有场景,关键在于权衡一致性、可用性与性能,并建立完善的监控和降级机制。
未经允许不得转载:任鹏个人博客 » MySQL 读写分离实现方案与主从一致性延迟处理

