14.5.3 query_rules匹配与优先级

在ProxySQL中,读写分离的核心在于mysql_query_rules表,它通过规则匹配将SQL路由到指定的hostgroup。规则匹配遵循“按优先级排序、逐条匹配、命中即停”的原则,因此理解匹配逻辑与优先级控制是确保路由准确的关键。

原理草图:优先级与命中流程#

文章图片

规则字段与匹配逻辑#

mysql_query_rules常用字段如下:
- rule_id:规则标识,通常与active配合管理规则启停。
- active:是否启用规则,0为禁用,1为启用。
- match_digest:基于SQL规范化后的指纹匹配,适合稳定语句。
- match_pattern:基于正则匹配原始SQL文本,灵活但性能较差。
- flagIN/flagOUT:规则标记传递与串联匹配。
- destination_hostgroup:命中后路由到的目标hostgroup。
- apply:是否终止后续规则匹配,1为终止,0为继续。
- priority:规则优先级,数值越小优先级越高。

匹配顺序由priority决定,若优先级相同则按rule_id排序。命中后若apply=1,立即停止匹配并执行路由;若apply=0,则继续向后匹配,可用于打标或多阶段规则。

priority与规则设计原则#

合理的优先级设计可避免误路由与性能损耗:
1. 高优先级处理例外:如强制主库写、事务内查询、特定表写入等应优先匹配。
2. 中优先级处理通用规则:如SELECT走从库、INSERT/UPDATE/DELETE走主库。
3. 低优先级作为兜底:未匹配规则的SQL统一路由到默认hostgroup。

推荐将priority分区:10–99用于特殊规则,100–199用于常规读写规则,900+用于兜底规则。

match_digest与match_pattern的取舍#

  • match_digest:基于规范化SQL指纹,稳定且性能好,适合通用规则。
  • match_pattern:基于正则,适合匹配特殊场景,但性能开销更高。

建议优先使用match_digest,仅在规则需要识别具体文本时使用match_pattern

完整示例:规则配置、加载与验证#

以下示例假设主库hostgroup=10,从库hostgroup=20,默认hostgroup=10。

1)安装与登录(用于执行规则)#

# Debian/Ubuntu
sudo apt-get install -y proxysql

# CentOS/RHEL
sudo yum install -y proxysql

# 登录到ProxySQL管理端(默认端口6032)
mysql -u admin -padmin -h 127.0.0.1 -P6032

命令解释:
mysql -u admin -padmin -h 127.0.0.1 -P6032 用于连接ProxySQL管理接口,执行规则与运行时加载。

2)插入规则(示例含优先级与flag传递)#

-- 清理旧规则(谨慎使用)
DELETE FROM mysql_query_rules;

-- 事务开始打标,flagOUT=1,apply=0 继续匹配
INSERT INTO mysql_query_rules
(rule_id, active, match_pattern, flagOUT, apply, priority)
VALUES
(10, 1, '^BEGIN|^START TRANSACTION', 1, 0, 10);

-- 事务内所有SQL强制主库(flagIN=1)
INSERT INTO mysql_query_rules
(rule_id, active, flagIN, destination_hostgroup, apply, priority)
VALUES
(11, 1, 1, 10, 1, 20);

-- 特定表强制主库(高优先级)
INSERT INTO mysql_query_rules
(rule_id, active, match_digest, destination_hostgroup, apply, priority)
VALUES
(20, 1, '^SELECT .* FROM order_detail', 10, 1, 30);

-- 写请求走主库(中优先级)
INSERT INTO mysql_query_rules
(rule_id, active, match_digest, destination_hostgroup, apply, priority)
VALUES
(100, 1, '^(INSERT|UPDATE|DELETE)', 10, 1, 100);

-- 读请求走从库(中优先级)
INSERT INTO mysql_query_rules
(rule_id, active, match_digest, destination_hostgroup, apply, priority)
VALUES
(110, 1, '^SELECT', 20, 1, 120);

-- 兜底规则(低优先级)
INSERT INTO mysql_query_rules
(rule_id, active, destination_hostgroup, apply, priority)
VALUES
(900, 1, 10, 1, 900);

命令解释:
- flagOUT=1 将事务打标;flagIN=1 命中事务内SQL。
- apply=1 表示命中后停止匹配;priority越小越先匹配。

3)加载与保存#

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

命令解释:
- LOAD ... TO RUNTIME 让规则立即生效
- SAVE ... TO DISK 持久化规则,重启不丢失

4)验证与预期效果#

-- 查看运行时规则
SELECT rule_id, priority, active, match_digest, match_pattern,
       flagIN, flagOUT, destination_hostgroup, apply
FROM runtime_mysql_query_rules
ORDER BY priority, rule_id;

-- 观察规则命中次数
SELECT rule_id, hits, last_match
FROM stats_mysql_query_rules
ORDER BY hits DESC;

-- 观察SQL指纹与路由统计
SELECT hostgroup, digest_text, count_star
FROM stats_mysql_query_digest
ORDER BY count_star DESC
LIMIT 5;

预期效果:
- 事务内查询路由到hostgroup=10
- 普通SELECT路由到hostgroup=20
- INSERT/UPDATE/DELETE路由到hostgroup=10

排错清单(常见问题与定位命令)#

  1. 规则不生效:是否忘记LOAD MYSQL QUERY RULES TO RUNTIME
  2. 优先级错乱:检查priorityrule_id排序
  3. 规则命中为0:验证match_digest是否与实际指纹一致
  4. 误路由到默认hostgroup:检查是否缺少兜底规则

定位命令:

-- 查看SQL规范化指纹与原始SQL
SELECT digest_text, query
FROM stats_mysql_query_digest
ORDER BY count_star DESC
LIMIT 5;

-- 查看当前连接的hostgroup
SELECT hostgroup, srv_host, srv_port, status
FROM stats_mysql_connection_pool;

练习#

  1. SELECT * FROM user_profile强制路由到主库(priority=40),验证是否优先于通用SELECT规则。
  2. 使用flagIN/flagOUT实现“事务内全部走主库”,并观察stats_mysql_query_rules的命中次数变化。
  3. 设计一条兜底规则,将未匹配SQL路由到hostgroup=10,观察命中统计。