ProxySQL实现读写分离的关键在于mysql_servers、mysql_replication_hostgroups、mysql_query_rules三张表的联动闭环:需确保runtime_mysql_servers有记录,runtime_mysql_replication_hostgroups中writer/reader_hostgroup与mysql_servers的hostgroup_id严格一致,且从库read_only=ON、max_replication_lag合理设置,否则将导致连不上主库或SELECT全走主库。

ProxySQL 能直接让 PHP 无感实现读写分离,但配置错一个字段就会连不上主库或 SELECT 全走主库——关键不是“配不配得出来”,而是 mysql_servers、mysql_replication_hostgroups、mysql_query_rules 这三张表的联动逻辑是否闭环。
ProxySQL 启动后连不上 MySQL?先查这三张运行态表
客户端能连上 ProxySQL(mysql -h127.0.0.1 -P6033),但执行 SQL 报 Host is down 或超时,90% 是后端节点没真正注册进运行时。别只看 mysql_servers 表里有没有数据,要验证三张表是否同步生效:
-
SELECT * FROM runtime_mysql_servers—— 必须有记录,空则说明LOAD MYSQL SERVERS TO RUNTIME没执行 -
SELECT * FROM runtime_mysql_replication_hostgroups—— 必须存在且writer_hostgroup = 1、reader_hostgroup = 2,否则自动路由失效 -
SELECT * FROM runtime_mysql_query_rules—— 如果你加了规则,这里必须有匹配项;如果没加规则,这条可为空,靠 hostgroup 自动分发
常见错误:INSERT 进 mysql_servers 时把所有节点 hostgroup_id 都设成 0,或漏掉 INSERT INTO mysql_replication_hostgroups 这一步。
SELECT 全部走主库?检查 mysql_replication_hostgroups 是否生效
默认情况下 ProxySQL 不依赖 SQL 解析,而是靠 mysql_replication_hostgroups + read_only 变量做 hostgroup 级路由。也就是说,只要从库 read_only = ON 且被正确归入 reader_hostgroup = 2,所有 SELECT 就会自动发到该组——前提是没被其他规则拦截。
立即学习“PHP免费学习笔记(深入)”;
- 确认从库 MySQL 实例中执行
SELECT @@read_only返回1 - 检查
mysql_servers表中从库记录的hostgroup_id值是否为 2(不是 0 或 1) - 确认
mysql_replication_hostgroups中writer_hostgroup和reader_hostgroup的值与mysql_servers中的hostgroup_id完全一致 - 如果加了
mysql_query_rules,确保其destination_hostgroup字段等于reader_hostgroup(比如 2),否则规则无效
从库延迟大,SELECT 还在转发?用 max_replication_lag 控制
ProxySQL 默认只认 read_only,不感知 Seconds_Behind_Master。即使从库延迟 10 秒,只要 read_only = ON,它就继续转发 SELECT——这是线上最隐蔽的脏读源头。
- 在
mysql_servers表中为每个从库显式设置max_replication_lag(单位:秒),例如UPDATE mysql_servers SET max_replication_lag = 300 WHERE hostgroup_id = 2 - ProxySQL 会定期执行
SHOW SLAVE STATUS,若发现某从库Seconds_Behind_Master > max_replication_lag,自动将其status改为OFFLINE_HARD,不再参与负载 - 注意:该机制依赖 MySQL 用户权限,ProxySQL 连接从库的账号需有
REPLICATION CLIENT权限
PHP 应用里怎么强制某次 SELECT 走主库?用注释或事务绑定
ProxySQL 本身不支持单条语句级强制路由(如 /* FORCE_MASTER */ SELECT ...),但可通过两种方式绕过:
- 在 SQL 开头加
/*+ MAX_EXECUTION_TIME(1) */类似 hint 不起作用;真正有效的是用/* !40101 SET @myrole='master' */配合自定义规则,但维护成本高 - 更可靠的做法是:在 PHP 中开启事务(哪怕只读),因为 ProxySQL 会将同一事务内所有语句发往同一个 hostgroup——而事务默认由主库发起,后续 SELECT 自然留在主库
- 或者干脆在 PHP 层控制:对强一致性场景(如登录后查用户信息),不走通用 DB 封装,直接 new PDO 连主库地址(
127.0.0.1:6033)并指定hostgroup_id=1的连接池
复杂点在于:延迟判断、规则优先级、hostgroup 状态同步都是异步发生的,SAVE MYSQL SERVERS TO DISK 后重启 ProxySQL 才算真正落地——别只信 runtime 表里的临时状态。



















