ProxySQL本身不校验SQL语义权限,真正的权限隔离由后端MySQL承担;它仅通过用户绑定、路由规则和查询过滤实现逻辑层访问路径隔离,需配合MySQL账户最小权限设计才能生效。

ProxySQL 本身不直接做「权限隔离」,它不校验 SQL 语义级权限(比如用户能否查某张表),也不替代 MySQL 的 GRANT 机制。真正的权限隔离必须由后端 MySQL 实例承担。但你可以在 ProxySQL 层通过路由规则 + 用户绑定 + 查询过滤,实现逻辑层面的访问路径隔离——这是生产中最常用、最可控的“中间件层权限控制”方式。
为什么不能靠 ProxySQL 做真正的权限管理
ProxySQL 的 mysql_users 表只存用户名、密码、默认 hostgroup 和事务持久化开关,它不解析或拦截 DROP TABLE、SELECT * FROM sensitive_table 这类语句。一旦认证通过,所有请求都原样转发给后端 MySQL;后续权限检查完全由 MySQL 自己执行。
常见误操作是:以为在 mysql_users 中把用户 default_hostgroup 设为 2(只读组),就等于“禁止写”。但只要该用户在 MySQL 里有 INSERT 权限,且发的是显式写语句(比如带 /*+ READ_ONLY */ 注释绕过规则),仍可能命中主库并成功执行。
- ProxySQL 不会重写或拒绝合法语法的写语句,除非你显式配置
mysql_query_rules拦截 - 它不感知表名、列名、WHERE 条件等上下文,仅能匹配 SQL 模式、用户、digest、hostgroup 等有限维度
- 若后端 MySQL 用户权限过大(如
root@'%'),中间件层再怎么路由也拦不住越权操作
用 mysql_query_rules 实现基于用户的读写路径隔离
核心思路:让不同用户固定走不同 hostgroup,再配合后端 MySQL 账户权限收窄。例如,业务只读用户 app_ro 只能连从库,且该账户在 MySQL 里只被授予 SELECT 权限。
实操步骤:
- 先在 MySQL 主从库上分别创建两个账号:
app_rw(主从都赋SELECT,INSERT,UPDATE,DELETE)和app_ro(仅从库赋SELECT,主库不建该用户) - 在
ProxySQL的mysql_users表中插入两行,default_hostgroup分别设为 1(写组)和 2(读组) - **关键一步**:加一条强制路由规则,确保
app_ro发来的任何语句都不可能落到 hostgroup 1
INSERT INTO mysql_query_rules (rule_id,username,destination_hostgroup,active,retries,match_digest,apply) VALUES (10,'app_ro',2,1,3,NULL,1);
这条规则优先级高(rule_id=10),无条件将 app_ro 所有请求导向 hostgroup 2。即使它发了 UPDATE,也会被转发到从库——而从库上该用户没有写权限,MySQL 直接报错 ERROR 1142 (42000): UPDATE command denied to user。
注意:match_digest 为空表示不依赖 SQL 内容匹配,纯靠用户名分流,更稳定。
避免规则冲突:rule_id 和 apply 字段必须配对使用
很多人加了多条规则却没生效,问题常出在 apply 字段逻辑上。ProxySQL 规则按 rule_id 升序匹配,遇到第一个 apply=1 就停止匹配。如果你写了:
INSERT INTO mysql_query_rules (rule_id,username,destination_hostgroup,apply) VALUES (5,'app_rw',1,0); INSERT INTO mysql_query_rules (rule_id,username,destination_hostgroup,apply) VALUES (10,'app_ro',2,1);
那么 app_rw 请求会匹配 rule_id=5,但因 apply=0 继续往下找;接着匹配 rule_id=10,发现用户名不对,跳过;最终 fallback 到 default_hostgroup——这就不符合预期。
- 每个用户至少有一条
apply=1的兜底规则 - 如果要用正则或 digest 匹配写操作,务必把写规则(
apply=1)放在读规则之前,否则读规则先命中就不再继续 - 修改规则后必须执行
LOAD MYSQL QUERY RULES TO RUNTIME;和SAVE MYSQL QUERY RULES TO DISK;
真正起作用的其实是后端 MySQL 账户权限设计
ProxySQL 的路由只是“引导”,最终是否允许执行,全看后端 MySQL 返回什么错误。所以最易被忽略的一环是:你有没有在从库上删掉 app_rw 用户的写权限?有没有在主库上彻底删除 app_ro 用户?
验证方法很简单:
- 用
app_ro登录mysql -uapp_ro -p -h127.0.0.1 -P6033,执行UPDATE t1 SET x=1;—— 应该报错,而不是静默成功 - 用
app_rw登录,执行SELECT SLEEP(5);—— 查stats_mysql_query_digest,确认其hostgroup是 1,不是 2 - 查
monitor库的mysql_server_ping_log,确认从库没收到app_rw的写请求
中间件层的“权限隔离”本质是路由 + 权限的双重保险。少一环,隔离就漏风。



















