MySQL读写分离需以稳定主从复制为前提,通过路由层分发读写请求;主库须开启log-bin并设ROW格式,从库应设read_only=ON;延迟超5秒需排查瓶颈;ProxySQL等代理或Spring Boot AbstractRoutingDataSource可实现路由,但须规避事务内写后读不一致、时间戳查询、会话变量及hint未识别等陷阱。

MySQL 读写分离不是靠单条命令或一个配置开关就能启用的,它必须建立在主从复制已稳定运行的基础上,并通过请求路由层把 SELECT 和 INSERT/UPDATE/DELETE 分发到不同实例。没主从同步,读写分离就是空中楼阁;没路由逻辑,应用照样全打主库。
主从复制必须先跑通,且延迟可控
这是所有读写分离方案的前提,跳过这步直接配代理或改代码,只会放大不一致问题。
- 主库必须开启
log-bin、设置唯一server-id,推荐使用binlog-format = ROW - 从库需配置
read_only = ON,防止误写(但注意:超级用户仍可绕过,生产环境应限制权限) - 用
SHOW SLAVE STATUS\G检查Seconds_Behind_Master,持续 > 5s 的延迟说明复制链路有瓶颈(如大事务、网络抖动、从库磁盘慢) - 半同步复制(
rpl_semi_sync_master_enabled)能降低丢数据风险,但会增加写响应时间,需权衡
应用层实现:Spring Boot + AbstractRoutingDataSource
适合已有 Java 微服务、想精细控制路由逻辑的场景,但对“写后立刻读”类需求要手动兜底。
- 定义两个数据源:
masterDataSource(写)和slaveDataSource(读),后者可配多个并加负载均衡策略 - 继承
AbstractRoutingDataSource,重写determineCurrentLookupKey(),根据方法注解(如@ReadOnly)或事务状态返回 key - 关键陷阱:
@Transactional内的SELECT默认走主库(框架为保证一致性),但若事务里先INSERT后SELECT,从库还没同步完,必须强制走主库——不能只依赖注解,得结合上下文判断 - ShardingSphere-JDBC 可自动处理“事务内写后读”,但需引入额外依赖,且 SQL 解析有兼容性边界(如复杂子查询、自定义函数可能 fallback 到主库)
中间件代理:ProxySQL 是当前最实用的选择
对应用零侵入,规则灵活,适合多语言、遗留系统或需要统一管控的团队。
-
mysql_servers表中注册主从节点,明确标注hostgroup_id(如 10=write,20=read) - 在
mysql_query_rules中设置匹配规则:match_pattern = '^SELECT '→destination_hostgroup = 20;但必须加一条高优先级规则:match_pattern = '^SELECT .* FOR UPDATE$'→destination_hostgroup = 10(这类读锁操作必须走主库) - 健康检查不能只看连通性,要查
mysql_server_connect_error和mysql_server_max_replication_lag,否则延迟飙升的从库还在收读流量 - ProxySQL 自带连接池,但
max_connections配太小会导致客户端报Too many connections,需按后端 MySQL 的max_connections值反推并预留余量
最容易被忽略的三个点
它们不写在文档首页,却常导致上线后出现“数据查不到”“主库还是被打爆”“切换后读不到新数据”:
- 时间戳类业务(如订单创建后查最新 10 条)必须走主库,因为从库延迟无法承诺“最新”
- 任何涉及
LAST_INSERT_ID()、@@IDENTITY或会话变量的查询,不能路由到从库——这些值只存在于主库会话中 - 代理层或应用层做的读写分离,都默认假设 SQL 是标准语法;一旦用了
SELECT /*+ READ_FROM_MASTER */ ...这类 hint,就必须在路由规则里显式识别,否则会被忽略



















