proxysql在主从架构下按sql语义路由读写流量,不解决分布式一致性;需区分select和select ... for update规则,开启transaction_persistent保障事务内路由一致。

ProxySQL 不是“为了分布式而做读写分离”,而是在已有主从复制架构下,用它把读写流量按语义拆开,避免主库被查崩。它本身不解决分布式一致性、分片或跨节点事务,只做请求路由——这点必须先划清。
为什么不能直接让应用自己判断 SELECT/INSERT 路由?
多数业务代码里混着 SELECT 和 SELECT ... FOR UPDATE,甚至 ORM 自动生成的语句带隐藏写语义(比如 INSERT ... ON DUPLICATE KEY UPDATE)。靠应用层硬拆,极易漏判、误判。
常见错误现象:
- 把带锁读(
SELECT ... FOR UPDATE)发到从库 → 报错或返回过期数据 - 事务内混用主从连接 → 主从不一致 + 隐式提交
- 连接池未隔离,一个连接刚执行了
UPDATE,下一次SELECT还走这个连接 → 从库查不到最新结果
ProxySQL 在协议层解析 SQL,规则匹配优先级可控,且能感知事务状态(配合 transaction_persistent),比应用层更可靠。
match_digest 规则为什么必须区分 ^SELECT.*FOR UPDATE$ 和 ^SELECT?
这是最常踩的坑:只配一条 ^SELECT 规则,结果所有 SELECT 全进读组,包括 SELECT ... FOR UPDATE —— 它本质是写操作,必须走主库。
正确做法是两条规则,且 rule_id 小的优先匹配:
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
-
rule_id = 1:match_digest = "^SELECT.*FOR UPDATE$"→destination_hostgroup = 10(写组) -
rule_id = 2:match_digest = "^SELECT"→destination_hostgroup = 20(读组)
注意正则结尾的 $ 和开头的 ^,否则可能误中 INSERT SELECT 这类嵌套语句。
monitor 模块的 read_only 检测为什么不能全信?
ProxySQL 的 mysql_replication_hostgroups 表依赖 monitor 用户定期查 SELECT @@read_only 来自动归类节点。但问题在于:
- 从库
read_only=1是手动设的,如果 DBA 临时关掉(比如做维护),monitor会立刻把它踢出读组,但没通知上层是否允许切流 - 主库故障后手动提升某从库为主,若忘了改
read_only=0,monitor仍把它当从库用 → 写请求失败 - 延迟大的从库
read_only仍是 1,但数据已严重滞后,monitor不管这个
所以生产环境必须配 mysql_servers.check_type = 'ping' + 自定义延迟监控(比如查 seconds_behind_master),再结合 mysql_query_rules.apply = 1 手动干预。
事务中的读请求到底走哪?
默认情况下,ProxySQL 对事务内所有语句都路由到事务起始时所在的 hostgroup。但前提是:
- 用户在
mysql_users表中设置了transaction_persistent = 1 - 第一条语句是
START TRANSACTION或隐式开启(如SELECT前没其他语句) - 后续语句没跨
COMMIT/ROLLBACK边界
否则,单条 SELECT 可能被规则单独匹配,导致事务中途换库 —— 数据不一致风险极高。这个开关默认是关的,必须显式打开。
真正难处理的是长事务 + 主从延迟 + 强一致性要求的场景:这时连 SELECT 都得强制走主库,规则就得加条件字段(如 username 或 schemaname),而不是无差别匹配 ^SELECT。










