mysql原生不支持读写分离,必须依赖proxysql等中间件实现延迟感知的读负载均衡:通过monitor模块监控seconds_behind_master,自动将延迟超阈值的从库置为offline_hard,并结合mysql_query_rules强制事务内、注释标记、系统表查询等关键读请求直连主库。

MySQL原生不支持读写分离,必须依赖中间件做SQL解析和路由;单纯靠HAProxy或应用层硬判断SELECT开头会漏判、误判,高并发下极易把写请求发到从库或把关键读请求发到延迟从库。
为什么轮询(Round Robin)在从库间做负载均衡经常失效
轮询假设所有从库性能一致、延迟一致、负载一致——现实完全相反。一个刚完成LOAD DATA INFILE回放的从库可能延迟几十秒,但还在轮询池里;另一台配置低的从库CPU已95%,却仍被分到20%的流量。
- 延迟不感知:轮询不看
Seconds_Behind_Master,查不到最新数据是常态 - 资源不匹配:未考虑CPU、IO、连接数等实时指标,小规格从库容易打满
- 权重静态化:无法根据慢查询率、平均响应时间动态调权
ProxySQL 2.4+ 支持基于监控指标的自动权重调整,但默认关闭,需手动启用并配置mysql_servers表中的weight字段与status字段联动逻辑。
如何用ProxySQL实现带延迟感知的读负载均衡
核心不是“让请求均匀分配”,而是“让请求落到能及时响应的节点”。关键动作是把Seconds_Behind_Master纳入路由决策闭环。
- 先在
mysql_servers中为每个从库设置初始weight(如1000),并确保max_replication_lag设为合理阈值(如1秒) - 启用
monitor模块的replication_lag_max检查,它会定期执行SHOW SLAVE STATUS并更新mysql_servers.status - 当某从库
Seconds_Behind_Master > max_replication_lag时,ProxySQL自动将其status置为OFFLINE_HARD,不再参与任何读路由 - 配合
mysql_query_rules中apply=1和sticky_conn=1,避免同事务内跨节点读取造成不一致
示例语句:
UPDATE mysql_servers SET weight = 500 WHERE hostname = 'slave2' AND hostgroup_id = 20;之后必须执行
SAVE MYSQL SERVERS TO DISK和LOAD MYSQL SERVERS TO RUNTIME才生效。
哪些SQL必须强制走主库,不能交给负载均衡算法
负载均衡只管“分发”,不管“语义”。以下几类读操作,无论从库多空、延迟多低,都必须绕过读组直连主库:
- 事务内首次
SELECT(尤其BEGIN后紧跟SELECT ... FOR UPDATE)——否则会触发隐式锁升级失败 - 带
/*+ FORCE_MASTER */注释的语句(ProxySQL可配置规则匹配该注释) - 查询系统表如
information_schema.PROCESSLIST或performance_schema,这些表不复制到从库 - 刚执行
INSERT后立即SELECT同一行(比如注册后查用户ID),此时从库大概率还没同步
别指望“最终一致性”兜底——业务上“查不到刚写的记录”就是错误,不是延迟问题。
真正难的不是配置轮询或加权,而是定义清楚哪些读可以下推、哪些必须钉死主库、以及如何让延迟判断快于业务超时阈值。ProxySQL的mysql_query_rules规则顺序和apply标志位稍配错,就可能让SELECT FOR UPDATE走到从库报错ERROR 1290 (HY000): The MySQL server is running with the --read-only option。











