1.了解鍊式規則
在mysql_query_rules表中,有兩個特殊字段"flagIN"和"flagOUT",它們分别用來定義規則的入口和出口,進而實作鍊式規則(chains of rules)。
鍊式規則的實作方式如下:
當入口值flagIN設定為0時,表示開始進傳入連結式規則。如未顯式指定規則的flagIN值,則預設都為0。
當語句比對完目前規則後,将記下目前規則的flagOUT值,如果flagOUT值非空(NOT NULL),則為該語句打上flagOUT标記。如果該規則的apply字段值不是1,則繼續向下比對。
如果語句的flagOUT标記和下一條規則的flagIN值不同,則跳過該規則,繼續向下比對。直到比對到flagOUT=flagIN的規則,則比對該規則。該規則是鍊式規則中的另一條規則。
直到某規則的apply字段設定為1,或者已經比對完所有規則,則最後一次被評估的規則将直接生效,不再繼續向下比對。
通過下面兩張圖,應該很容易了解鍊式規則的生效方式。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZyczNxM2YhRjZwUjYjFjZ2QjYzcDZ1AjZ1IDZ3UDNh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
必須注意,規則是按照rule_id的大小順序進行的。且并非隻有apply=1時才會應用規則,當無規則可比對,或者某規則的flagIN和flagOUT值相同,都會應用最後一次被評估的規則。
以下幾個示例,可以解釋生效規則:
# rule_id=3 生效
+---------+-------+--------+---------+
| rule_id | apply | flagIN | flagOUT |
+---------+-------+--------+---------+
| 1 | 0 | 0 | 23 |
| 2 | 0 | 23 | 23 |
| 3 | 0 | 23 | NULL |
+---------+-------+--------+---------+
# rule_id=2 生效
+---------+-------+--------+---------+
| rule_id | apply | flagIN | flagOUT |
+---------+-------+--------+---------+
| 1 | 0 | 0 | 23 |
| 2 | 0 | 23 | 23 |
| 3 | 0 | 24 | NULL |
+---------+-------+--------+---------+
# rule_id=2 生效,因為比對完rule_id=2後,還打着flagOUT=23标記
+---------+-------+--------+---------+
| rule_id | apply | flagIN | flagOUT |
+---------+-------+--------+---------+
| 1 | 0 | 0 | 23 |
| 2 | 0 | 23 | NULL |
| 3 | 1 | 24 | NULL |
+---------+-------+--------+---------+
# rule_id=3 生效,因為比對完rule_id=2後,還打着flagOUT=23标記
+---------+-------+--------+---------+
| rule_id | apply | flagIN | flagOUT |
+---------+-------+--------+---------+
| 1 | 0 | 0 | 23 |
| 2 | 0 | 23 | NULL |
| 3 | 1 | 23 | NULL |
+---------+-------+--------+---------+
2.鍊式規則示例
有了普通規則比對方式,為什麼還要設計鍊式規則呢?雖然ProxySQL通過正規表達式實作了很靈活的規則比對模式,但需求總是千變萬化的,有時候僅通過一條正則比對規則和替換規則很難實作比較複雜的要求,例如sharding時。
鍊式規則除了常用的多次替換,還可巧用于多次比對。
本文簡單示範一下鍊式規則,不具有實際意義,隻為後面ProxySQL實作sharding的文章做基礎知識鋪墊。
2個測試庫,共4張表test{1,2}.t{1,2}。
mysql> select * from test1.t1;
+------------------+
| name |
+------------------+
| test1_t1_malong1 |
| test1_t1_malong2 |
| test1_t1_malong3 |
+------------------+
mysql> select * from test1.t2;
+------------------+
| name |
+------------------+
| test1_t2_malong1 |
| test1_t2_malong2 |
| test1_t2_malong3 |
+------------------+
mysql> select * from test2.t1;
+--------------------+
| name |
+--------------------+
| test2_t1_xiaofang1 |
| test2_t1_xiaofang2 |
| test2_t1_xiaofang3 |
+--------------------+
mysql> select * from test2.t2;
+--------------------+
| name |
+--------------------+
| test2_t2_xiaofang1 |
| test2_t2_xiaofang2 |
| test2_t2_xiaofang3 |
+--------------------+
現在借用鍊式規則,一步一步地将對test1.t1表的查詢路由到test2.t2表的查詢。再次聲明,此處示例毫無實際意義,僅為示範鍊式規則的基本用法。
大緻鍊式比對的過程為:
test1.t1 --> test1.t2 --> test2.t1 --> test2.t2
以下是具體插入的規則:
delete from mysql_query_rules;
select * from stats_mysql_query_digest_reset where 1=0;
insert into mysql_query_rules
(rule_id,active,apply,flagIN,flagOUT,match_pattern,replace_pattern) values
(1,1,0,0,23,"test1\.t1","test1.t2");
insert into mysql_query_rules
(rule_id,active,apply,flagIN,flagOUT,match_pattern,replace_pattern) values
(2,1,0,23,24,"test1\.t2","test2.t1");
insert into mysql_query_rules
(rule_id,active,apply,flagIN,flagOUT,match_pattern,replace_pattern,destination_hostgroup) values
(3,1,1,24,NULL,"test2\.t1","test2.t2",30);
load mysql query rules to runtime;
save mysql query rules to disk;
admin> select rule_id,
apply,
flagIN,
flagOUT,
match_pattern,
replace_pattern,
destination_hostgroup DH
from mysql_query_rules;
+---------+-------+--------+---------+---------------+-----------------+------+
| rule_id | apply | flagIN | flagOUT | match_pattern | replace_pattern | DH |
+---------+-------+--------+---------+---------------+-----------------+------+
| 1 | 0 | 0 | 23 | test1\.t1 | test1.t2 | NULL |
| 2 | 0 | 23 | 24 | test1\.t2 | test2.t1 | NULL |
| 3 | 1 | 24 | NULL | test2\.t1 | test2.t2 | 30 |
+---------+-------+--------+---------+---------------+-----------------+------+
查詢test1.t1表,測試結果。
[[email protected] ~]# mysql -uroot [email protected]! -h127.0.0.1 -P6033 -e "select * from test1.t1;"
+--------------------+
| name |
+--------------------+
| test2_t2_xiaofang1 |
| test2_t2_xiaofang2 |
| test2_t2_xiaofang3 |
+--------------------+
admin> select * from stats_mysql_query_rules;
+---------+------+
| rule_id | hits |
+---------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
+---------+------+
admin> select hostgroup,digest_text from stats_mysql_query_digest;
+-----------+----------------------------------+
| hostgroup | digest_text |
+-----------+----------------------------------+
| 30 | select * from test2.t2 |
+-----------+----------------------------------+
顯然,已經按照預想中的方式進行比對、替換、路由。
一個問題:如果查詢的是test1.t2表或test2.t1表,會進行鍊式比對嗎?
答案是不會,因為rule_id=2和rule_id=3這兩個規則的flagIN都是非0值,而每個SQL語句初始時隻進入flagIN=0的規則。
此外還需注意,當某語句未按照我們的期望途經所有的鍊式規則,則可能會根據destination_hostgroup字段的值直接路由出去,即使沒有指定該字段值,還有使用者的預設路由目标組,或者基于端口的路由目标。是以,在寫鍊式規則時,應當盡可能地針對某一類型的語句進行完完整整的定制,保證這類語句能途經我們所期望的所有規則。