读写分离需依赖中间件或应用层实现,mysql单实例不支持自动读写分离;常见方案包括shardingsphere-jdbc、mycat、proxysql、maxscale及手动多数据源路由,其中proxysql因低侵入性和稳定性成为主流选型,但配置易错,需关注主从识别、路由规则、事务粘性及延迟兜底等关键点。

读写分离不是MySQL自带的功能,得靠中间件或应用层拆分
MySQL单实例本身不提供自动读写分离能力。所谓“读写分离”,本质是把 SELECT 请求发到从库,INSERT/UPDATE/DELETE 发到主库——这个路由逻辑必须由外部组件完成。
常见错误现象:SELECT 还是打到主库、从库延迟导致读到旧数据、事务内混用读写导致不一致。
- 应用直连方式:用
ShardingSphere-JDBC或MyCat做 JDBC 层路由,配置简单但升级/调试依赖 SDK 版本 - 代理层方式:部署
ProxySQL或MaxScale,对应用无侵入,但多一跳网络、需额外维护节点健康状态 - 手动控制:在代码里显式指定数据源(如 Spring 的
@Transactional(readOnly = true)+ 多数据源路由),灵活性高,但容易漏写、难统一治理
ProxySQL 是目前最稳的中间件选型,但配置项容易配错
它不像 MyCat 那样需要改 SQL 语法,也不像 ShardingSphere 那样深度耦合应用生命周期,适合已上线系统快速接入。
常见错误现象:mysql_servers 表没设 weight 导致流量全打一台从库、mysql_replication_hostgroups 没启用导致从库不被识别为可读节点、忘记 reload mysql_query_rules 导致规则不生效。
- 主从识别靠
read_only系统变量,务必确保从库开启read_only=1,否则 ProxySQL 不会把它划进读组 - 路由规则优先级靠
rule_id升序匹配,match_digest写^SELECT比写SELECT更安全,避免误匹配INSERT SELECT - 监控必须接
stats_mysql_connection_pool表,光看SHOW POOLS容易误判连接是否真分发到了从库
从库延迟大时,强制走主库读是最实际的兜底方案
业务上不是所有 SELECT 都能容忍延迟。比如用户刚注册完立刻查个人资料,或者支付成功后查订单状态,这类场景必须读主库,否则体验崩坏。
常见错误现象:用 /*+ FORCE_MASTER */ 注释但 ProxySQL 没开 mysql-query_rules.fast_forward,注释直接被忽略;或者用了 hint 却没在 mysql_query_rules 里加对应规则。
- ProxySQL 支持 SQL 注释识别,但默认关闭。要先执行:
UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-query_rules.fast_forward';,再LOAD MYSQL VARIABLES TO RUNTIME - 规则中
match_pattern可写/*\+ FORCE_MASTER \*/(注意转义),destination_hostgroup设为主库 hostgroup ID - 别依赖“自动降级”——没有中间件能可靠判断“这行数据我刚写过,现在必须读主库”。该加注释的地方,代码里就得加
事务里所有读操作默认走主库,但嵌套事务或连接池复用可能破坏这点
ProxySQL 和 ShardingSphere 都约定:一旦连接进入事务(BEGIN 或 START TRANSACTION),后续所有语句都路由到主库。这是正确行为,但容易被现实绕过。
常见错误现象:Spring @Transactional 方法里调了另一个 @Transactional(propagation = Propagation.REQUIRES_NEW) 方法,外层事务提交后连接被归还池中,内层新开连接却没走主库;或者 Druid 连接池开了 testOnBorrow,执行 SELECT 1 探活时意外触发读路由到从库,污染了事务上下文。
- 确认中间件是否支持事务粘性(transaction stickiness)。ProxySQL 默认支持,但要求客户端不显式
COMMIT后还复用连接 - 连接池配置里禁用自动探活语句,或确保探活语句走的是独立连接(如 Druid 的
validationQuery设为/*+ FORCE_MASTER */ SELECT 1) - 不要在事务中调用非本库的查询服务(比如查 Redis 或调 HTTP 接口),否则事务边界和读库选择逻辑就彻底脱钩了
SELECT 该走从库、哪条必须钉死主库——这得一行行看业务逻辑,没法靠中间件自动猜。










