读写分离落地后,负载均衡的配置重心必须从“分摊流量”转向“识别读写”,核心答案是:在数据库中间件层做读写路由,在应用层做流量收敛,在连接池层做资源隔离,三者协同才能避免“分离了却均衡不了”的尴尬。
很多团队把读写分离做完,发现主库负载没降多少,从库倒是闲得发慌,问题不在数据库,而在流量根本没按预期分配,这篇文章不聊理论,直接讲配置路径和踩坑点。
读写分离后负载均衡怎么配置:先分清三个层面的职责
读写分离后的负载均衡,和普通Web服务的负载均衡是两码事,普通LB看的是请求均匀不均匀,数据库LB看的是这条SQL该不该落到这个节点,配置前,先明确三个层面的分工:
- 接入层(应用侧):负责把读请求和写请求打上标记,或者直接分流到不同的数据源。
- 中间件层(Proxy):负责解析SQL语义,SELECT走从库,INSERT/UPDATE/DELETE走主库,事务内的读必须走主库。
- 连接池层(驱动侧):负责连接复用和节点健康检查,避免某个从库挂了还在往里塞请求。
行业共识认为,绝大多数中小团队的问题出在第二层中间件选型或规则配置不当,如果你们还在用应用层硬编码路由,趁早换掉。
中间件选型:读写分离数据库负载均衡方案的主流对比
市面上成熟的方案就那几类,选型决定了配置复杂度,直接说结论:
| 方案 | 读写分离支持 | 负载均衡算法 | 适用规模 | 配置难度 |
|---|---|---|---|---|
| MySQL官方ProxySQL | 原生支持 | 权重、最少连接数、ping | 中大型 | 中 |
| MyCat | 原生支持 | 轮询、权重 | 中型 | 中高 |
| ShardingSphere-JDBC | 应用内嵌 | 轮询、随机、权重 | 中小型 | 低 |
| MaxScale | 原生支持 | 权重、连接数 | 中大型 | 中 |
选型建议:Java技术栈、不想多维护一个中间件节点,用ShardingSphere-JDBC,配置简单且支持读写分离和负载均衡算法切换。异构语言或需要统一管控入口,选ProxySQL,它能实时查看路由结果,调试方便。
中间件层配置实例:ProxySQL的读写分离和负载均衡规则
以ProxySQL为例,配置分三步,每一步都有坑。
第一步:定义节点组和权重
mysql_servers: - hostgroup_id: 10 # 写组 hostname: 192.168.1.10 port: 3306 weight: 100 - hostgroup_id: 20 # 读组 hostname: 192.168.1.11 port: 3306 weight: 60 - hostgroup_id: 20 hostname: 192.168.1.12 port: 3306 weight: 40
权重不是随便拍的。权重值建议按从库的硬件配置和当前QPS来定,比如机器A是8核16G,机器B是4核8G,权重给60:40比较合理,如果两个从库配置一样,权重默认相等即可,不必纠结。
第二步:写路由规则
mysql_query_rules:
- rule_id: 1
active: 1
match_pattern: "^SELECT . FOR UPDATE"
destination_hostgroup: 10
- rule_id: 2
active: 1
match_pattern: "^SELECT"
destination_hostgroup: 20
- rule_id: 3
active: 1
match_pattern: "."
destination_hostgroup: 10
规则顺序很关键。FOR UPDATE锁查询必须排在最前面,否则会被当成普通SELECT路由到从库,导致锁失效,业务中如果有SELECT ... LOCK IN SHARE MODE,同样要加规则,这是新手最容易忽略的配置项。
第三步:启用和验证
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SELECT FROM stats_mysql_query_rules;
验证方式:在ProxySQL的stats_mysql_connection_pool表里查看各节点的连接数和查询数,如果从库的Queries列长期为0,说明路由规则没生效。
从库负载不均的常见原因:配置对了但流量还是歪的
规则配置正确,不代表流量就均衡了,实际运维中,下面几个场景很典型:
- 连接池复用导致热点从库:应用侧连接池最小空闲连接数设置过大,连接全建立在一个从库上,另一个从库饿死,把
minimumIdle调小,让连接按需创建。 - 长事务绑定连接:从库A上的一个慢查询占住连接不放,新请求只能往从库B走,排查慢查询,对超过阈值的SQL做限流或超时断开。
- 权重配置和实际吞吐不匹配:权重60的机器是机械盘,权重40的是SSD,实际吞吐后者反而更高,配置权重前,先压测,别拍脑袋。
连接池层面的隔离策略
连接池是读写分离后的第一道关卡,业内专家指出,大多数负载不均的根源在连接池参数而非路由规则,配置时注意:
- 读写数据源必须用独立的连接池,不能让读写共用一套连接。
- 读连接池的
maxActive建议设置为主库的5到2倍,因为读并发通常远高于写。 - 从库连接池开启
testOnBorrow,节点宕机时快速摘除,避免请求卡死。
以Druid为例:
# 读库连接池 spring.datasource.read.max-active=100 spring.datasource.read.min-idle=10 spring.datasource.read.test-on-borrow=true # 写库连接池 spring.datasource.write.max-active=50 spring.datasource.write.min-idle=5 spring.datasource.write.test-on-borrow=true
读写分离后CPU负载不均衡的排查思路
有时候从库CPU跑到80%,主库才20%,表面看“均衡”了,实际可能是读流量全压在一个从库上,排查路径按这个顺序来:
- 看中间件统计:ProxySQL执行
SELECT FROM stats_mysql_connection_pool,对比各从库的Connections_used。 - 看数据库侧连接:登录每个从库执行
SHOW PROCESSLIST,统计来源IP的连接数。 - 看应用侧日志:确认是否存在某个服务模块固定连接了某个从库IP,比如配置了
read-host列表但没做随机。
负载均衡算法选择:轮询还是最少连接数?
这是读写分离数据库负载均衡方案里被问得最多的问题,直接给建议:
- 业务读请求耗时相近,选轮询或加权轮询,简单且可预期。
- 读请求耗时长尾明显(报表查询、批量导出),选最少连接数算法,避免慢查询堆积在同一个节点。
- 从库硬件异构,选加权轮询,权重按压测结果调整。
ShardingSphere-JDBC配置最少连接数:
spring.shardingsphere.rules.readwrite-splitting.data-sources.ds.load-balancer-name=least_connections
spring.shardingsphere.rules.readwrite-splitting.load-balancers.least_connections.type=LEAST_CONNECTIONS
注意:ShardingSphere的负载均衡算法作用于连接级别,不是请求级别。一个连接上的连续查询会固定路由到同一个从库,如果连接池复用率高,长尾流量还是可能堆积,这时候需要结合@Transactional(readOnly = true)强制走读库连接。
缓存层配合:减轻从库压力的隐藏策略
读写分离不是终点,从库负载高的另一大原因是大量重复查询直接打到数据库,而缓存层的命中率过低,配置读写分离时,顺手把缓存策略一起调整:
- 读多写少的数据(商品详情、用户资料),缓存TTL设为5到10分钟,用后台任务主动刷新。
- 实时性要求高的数据(库存、余额),缓存TTL设为几秒,且写操作后主动失效。
- 缓存失效时,加分布式锁防止缓存击穿,否则流量瞬间穿透到从库。
缓存命中率上来之后,从库的QPS能降一个数量级,这时候你会发现负载均衡配置反而没那么敏感了因为流量峰值被缓存削平了。
故障转移和摘除策略:负载均衡的底线保障
负载均衡配置得再好,节点挂了不自动摘除,一切都白搭,配置健康检查时注意:
- 探活SQL不要用
SELECT 1
,要模拟业务查询,比如
SELECT id FROM user WHERE id = 1,否则从库假死(连接正常但SQL卡住)时检测不到。 - 摘除阈值设为连续失败3次,避免单次网络抖动就误摘。
- 摘除后要自动恢复,但恢复前先确认从库的复制延迟已追上,否则查询结果会不一致。
ProxySQL的配置:
mysql_replication_hostgroups:
- writer_hostgroup: 10
reader_hostgroup: 20
check_type: read_only
check_interval: 2000
这里用的是read_only检查,当从库变成只读状态时自动摘除,比单纯ping更可靠。
读写分离后如何验证负载均衡是否生效
配置完成后,别急着上线,按下面的步骤验证一遍:
- 开启中间件的查询日志或监控面板。
- 压测工具分别跑读写混合流量,观察主库和从库的QPS曲线。
- 手动Kill一个从库进程,观察请求是否自动迁移到其他从库。
- 检查从库的复制延迟,确认无堆积。
一个简单有效的判断方法:主库的Threads_connected稳定在一个较低水平,从库之间Threads_connected的差值不超过20%,说明均衡策略基本生效。
Q&A:读写分离后负载均衡策略配置常见疑问
问:读写分离后,从库负载仍然很高怎么办?
答:先排查是否存在热点从库,查看中间件统计和连接池配置,如果各从库负载均衡,但整体负载仍高,说明读流量超出了从库集群容量,此时应增加从库节点或引入缓存层,不要试图通过调低主库负载来“平衡”,那是掩盖问题。
问:读写分离中间件本身会成为性能瓶颈吗?
答:会,ProxySQL这类中间件引入了一层额外的网络跳转和SQL解析开销,单节点吞吐上限通常在一两万QPS左右,如果读流量远超这个量级,考虑使用ShardingSphere-JDBC这种应用内嵌方案,或者对中间件做集群部署,具体选型要看业务规模和团队运维能力。
问:读写分离和分库分表能同时配置负载均衡吗?
答:可以,ShardingSphere同时支持读写分离和分库分表,读写分离的负载均衡算法作用于每个分片内的从库节点,配置时注意分片键和读写路由规则的优先级,先按分片键路由到目标库组,再在库组内做读写分离和负载均衡。
读写分离的负载均衡没有一劳永逸的配置,它随着业务流量特征的变化需要持续调整,先把中间件选型定好,再按上述步骤逐层配置和验证,最后用监控数据驱动调优这才是靠谱的落地路径,核心记住一句话:均衡不是目的,让每个节点干它该干的活才是目的。

