mysql在高可用架构中使用主从复制实现读写分离

来源:这里教程网 时间:2026-02-28 20:44:05 作者:

主从复制不是读写分离的自动开关

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
默认关掉,否则可能因短暂延迟拒绝切换——这在高并发写入时很常见

主从复制的可靠性边界很清晰:它解决单点故障,不解决脑裂、不保证实时、不自动修复应用路由。把“能切”当成“已切好”,是线上事故最常踩的坑。

相关推荐