MySQL 主从复制本身不提供读写分离能力
主从复制只是把主库的写操作通过 binlog 同步到从库,它不参与 SQL 路由、连接分发或负载决策。读写分离需要额外组件在应用层或中间件层完成请求识别与转发。
常见错误现象:
SELECT语句仍发到主库、从库延迟导致读到旧数据、事务内混用读写引发不一致。 应用直连多实例时,必须自己判断
INSERT/UPDATE/DELETE走主库,
SELECT走从库(需注意显式事务和
SELECT ... FOR UPDATE) 用
ProxySQL或
MaxScale时,要配置
mysql_query_rules匹配
SELECT并路由到
hostgroup 2(从库组),否则默认全走主库 Spring Boot +
AbstractRoutingDataSource需重写
determineCurrentLookupKey(),且
@Transactional方法内默认强制走主库,否则可能报错
从库延迟会直接破坏读写分离效果
主从延迟不是理论风险,而是常态。一旦
Seconds_Behind_Master> 0,应用从从库读取刚写入的数据就会失败——比如用户注册后立刻查个人页,返回 404。
使用场景中容易忽略:高并发写入、大事务、从库 IO 或 CPU 过载、网络抖动都会放大延迟。
监控必须包含SHOW SLAVE STATUS\G中的
Seconds_Behind_Master和
Exec_Master_Log_Pos与主库
File/
Position的差值 关键业务读操作可加
SELECT /*+ MAX_EXECUTION_TIME(1000) */ ...配合超时重试,或临时切回主库 避免在从库执行
pt-online-schema-change等长耗时操作,这类操作会阻塞 SQL 线程
GTID 模式下主从切换后读写分离更难维持
启用
gtid_mode=ON后,主从关系可通过
GTID_SUBSET自动定位位点,但读写分离中间件往往无法感知新主库身份,仍按旧拓扑转发请求。
典型问题:
CHANGE MASTER TO ... GTID_SET执行后,ProxySQL 的
mysql_servers表未更新,导致 SELECT 继续发向已下线的旧从库。 切换前必须调用
SAVE MYSQL SERVERS TO DISK并确认
mysql_servers中
status字段为
ONLINE或
SHUNNEDAnsible 或脚本触发 failover 时,需同步更新中间件配置 + 应用侧数据源地址(如 Spring Cloud Config 中的
spring.datasource.url) 不要依赖
read_only=ON判断主从角色——故障切换后新主库可能仍残留该参数,造成写入被拒
真正可用的读写分离必须处理事务一致性边界
一个事务里既有写又有读,或者读操作依赖刚写的值(如生成订单号后立即查详情),此时强行把
SELECT转给从库必然出错。
这是最容易被“自动读写分离”宣传误导的地方:没有中间件能 100% 正确解析 SQL 语义并保证事务上下文完整。
ShardingSphere-JDBC 的master-slave规则默认在事务内禁用从库路由,但若用
@Transactional(propagation = Propagation.NOT_SUPPORTED)就会失效 MyCat 不支持跨库事务,遇到
XA START会直接报错
ERROR 1105 (HY000): Unknown command最稳妥的做法是:对强一致性读,显式指定数据源(如
DataSourceType.MASTER),而非依赖自动识别 实际部署中,延迟监控、事务边界识别、failover 后配置同步这三点,比选型更消耗运维精力。
