MySQL主从复制仅同步数据,不自动实现读写分离;需应用层或中间件显式路由SELECT至从库,否则所有请求仍打向主库。

主从复制不是读写分离的自动开关
MySQL 主从复制本身只负责数据同步,不自带读写路由能力。应用层或中间件必须明确区分 SELECT 和 INSERT/UPDATE/DELETE 请求,否则所有流量仍打向主库,从库形同虚设。
常见错误是误以为开启 CHANGE MASTER TO 后“自然就分开了”,结果监控里看到从库 Seconds_Behind_Master 持续上涨,而主库压力纹丝不动——其实是没改应用代码或没配代理。
- 应用直连场景:需在业务代码中手动拆分连接池,写用
masterDataSource,读用slaveDataSource - 使用
ProxySQL或MaxScale时,必须配置mysql_query_rules规则,匹配SELECT才转发到hostgroup 2(从库组) - ORM 如 MyBatis + Spring,容易忽略
@Transactional(readOnly = true)不一定触发从库路由,得看具体AbstractRoutingDataSource实现逻辑
从库延迟导致读不到最新数据
Seconds_Behind_Master 显示为 0 并不等于强一致性。网络抖动、大事务回放、从库 IO 能力弱于主库,都会造成“伪实时”。尤其在订单创建后立刻查详情的场景,极易读到空结果。
这不是配置问题,是架构约束。解决思路不是压低延迟(极限也难到毫秒级),而是分类处理:
- 对刚写入就必须读的请求(如“提交订单→查订单号”),强制走主库,加
/*+ FORCE_MASTER */注释或调用专用主库接口 - 报表类、用户中心等非关键读,容忍几秒延迟,直接走从库
- 避免在事务内混用主从连接——Spring 的
@Transactional默认绑定单个数据源,跨源事务会破坏 ACID
主从切换后读请求仍在旧主库上失败
传统 MHA 或 Orchestrator 完成故障转移后,新主库 IP/端口变了,但客户端连接池还缓存着旧地址。此时 SELECT 报错 ERROR 2003 (HY000): Can't connect to MySQL server 或持续超时。
根本原因在于连接池无感知。应对方式取决于架构层级:
- 应用侧:用支持动态重连的驱动(如 MySQL Connector/J 8.0+ 的
autoReconnect=true&failOverReadOnly=false),但仅缓解,不根治 - 中间件侧:ProxySQL 支持
mysql_servers表热更新,配合 Orchestrator 的回调脚本可自动刷新后端列表 - DNS 层:将
master.db解析指向 VIP,VIP 漂移到新主库,要求应用全用域名连接且禁用 DNS 缓存(connectTimeout=3000配合cachePrepStmts=false)
半同步复制不能代替高可用决策
启用 rpl_semi_sync_master_enabled=ON 只保证至少一个从库收到 binlog,不保证它已执行完。当主库崩溃,那个“已接收”的从库可能 Exec_Master_Log_Pos 还卡在中间,强行提升会导致数据丢失。
真正决定能否切换的是 GTID 或位点比对:
- 用
SHOW SLAVE STATUS\G对比Retrieved_Gtid_Set和Executed_Gtid_Set,差集为空才说明完全追平 - 若用传统 binlog 文件+位置,需确认
Master_Log_File == Relay_Master_Log_File && Read_Master_Log_Pos == Exec_Master_Log_Pos - MHA 的
--check_repl_delay默认关掉,否则可能因短暂延迟拒绝切换——这在高并发写入时很常见
主从复制的可靠性边界很清晰:它解决单点故障,不解决脑裂、不保证实时、不自动修复应用路由。把“能切”当成“已切好”,是线上事故最常踩的坑。










