MySQL Rewriter Query Rewrite Plugin

eric0435發表於2020-04-08

從MySQL 5.7.6開始,MySQL伺服器支援查詢重寫外掛它可以在伺服器執行語句之前可以檢查和可能修改接收到的語句。MySQL包含一個名為Rewriter的查詢重寫外掛和安裝外掛與它相關元件的指令碼。這些元件一起工作提供對select的重寫能力:
.服務端外掛名為Rewriter檢查select語句並且可能基於快取在記憶體中的重寫規則來重寫它們。標準select語句和預備語句中的select語句可能經受重寫。出現在檢視定義中或儲存過程中的select語句不會經受重寫。

.Rewriter外掛使用一個包含rewrite_rules表的query_rewrite資料庫。表提供了對規則的永久儲存,外掛使用它來決賽是否重寫語句。透過儲存在表中的規則集讓使用者與外掛通訊。透過設定表中記錄的message列來讓使用者與外掛通訊。

.query_rewrite資料庫包含一個名為flush_rewrite_rules()的儲存過程用來把規則表中的內容載入到外掛中。

.使用者定義函式load_rewrite_rules()被flush_rewrite_rules()儲存過程來呼叫。

.Rewriter外掛顯示了系統變數能讓外掛配置和狀態變數來提供執行時操作資訊。

下面將描述如何安裝與使用Rewriter外掛並提供與它相關元件的資訊。

安裝或解除安裝Rewriter查詢重寫外掛
為了安裝或解除安裝Rewriter查詢重寫外掛,在MySQL安裝目錄下的share目錄中選擇執行合適的指令碼:
.install_rewriter.sql: 使用這個指令碼來安裝Rewriter外掛和它相關的元件。
.uninstall_rewriter.sql:使用這個指令碼來解除安裝Rewriter外掛和它相關的元件。

執行安裝指令碼

[mysql@localhost share]$ mysql -uroot -p < install_rewriter.sql
Enter password:

執行安裝指令碼將會安裝與啟用外掛。為了驗證它,連線到資料庫執行以下語句:

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| binlog |
| mysql |
| performance_schema |
| query_rewrite |
| sys |
+--------------------+
6 rows in set (0.00 sec)
mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | ON |
+------------------+-------+
1 row in set (0.00 sec)

使用Rewriter查詢重寫外掛
為了啟用或禁用Rewriter查詢重寫外掛可以透過啟用或禁用rewriter_enabled系統變數來完成。預設情況是當你安裝Rewriter查詢重寫外掛時是啟用的。為了顯式設定初始化Rewriter查詢重寫外掛的狀態,可以在伺服器啟動時設定rewriter_enabled變數。例如為了在選項檔案中啟用Rewriter外掛可以進行以下設定:
[mysqld]
rewriter_enabled=ON

也可以在執行時啟用或禁用Rewriter外掛:

mysql> set global rewriter_enabled=OFF;
Query OK, 0 rows affected (0.00 sec)
mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | OFF |
+------------------+-------+
1 row in set (0.00 sec)
mysql> set global rewriter_enabled=ON;
Query OK, 0 rows affected (0.00 sec)
mysql> show global variables like 'rewriter_enabled';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| rewriter_enabled | ON |
+------------------+-------+
1 row in set (0.01 sec)
mysql> desc query_rewrite.rewrite_rules;
+--------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| pattern | varchar(10000) | NO | | NULL | |
| pattern_database | varchar(20) | YES | | NULL | |
| replacement | varchar(10000) | NO | | NULL | |
| enabled | enum('YES','NO') | NO | | YES | |
| message | varchar(1000) | YES | | NULL | |
| pattern_digest | varchar(32) | YES | | NULL | |
| normalized_pattern | varchar(100) | YES | | NULL | |
+--------------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
mysql> select * from query_rewrite.rewrite_rules;
Empty set (0.00 sec)

假設Rewriter外掛被啟用,它將檢查和可能修改由伺服器所接收到的每個select語句。外掛將基於記憶體中快取的重寫規則(從query_rewriter資料庫的rewrite_rules表中載入的)來決定是否重寫語句。

新增重寫規則
為了向Rewriter外掛新增規則,向rewrite_rules表中新增記錄,然後呼叫flush_rewrite_rules()儲存過程來從表中載入規則到外掛中。下面的例子來建立一個簡單規則來匹配單個文字值的查詢語句。

mysql> insert into query_rewrite.rewrite_rules(pattern,replacement) values('select ?','select ?+1');
Query OK, 1 row affected (0.03 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from query_rewrite.rewrite_rules;
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
| id | pattern | pattern_database | replacement | enabled | message | pattern_digest | normalized_pattern |
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
| 1 | select ? | NULL | select ?+1 | YES | NULL | NULL | NULL |
+----+----------+------------------+-------------+---------+---------+----------------+--------------------+
1 row in set (0.00 sec)
mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: NULL
normalized_pattern: NULL
1 row in set (0.00 sec)

規則指定一種模式模板指示那種查詢語句會被匹配,並且替換模板指示瞭如何重寫匹配的語句。然而新增規則到rewrite_rules表中不足以造成Rewriter外掛使用這個規則。我們必須要呼叫flush_rewrite_rules()過程來將規則表的內容載入到外掛記憶體快取中:

mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.04 sec)

當外掛從規則表中讀取每種規則時,它將計算一個標準化(語句摘要)格式的模式和一個摘要雜湊值並使用它們來更新normalized_pattern和pattern_digest列:

mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)

模式使用與預備語句相同的語法。使用模式模板,?字元實際上作為引數標記用來匹配資料值。引數標記只能用於應該出現資料值的地方,而不能用於SQL關鍵字、識別符號等.?字元不應該使用引號括起來。

像模式一樣,替換可以包含?字元。對於匹配一種模式模板的語句,重寫外掛將重寫它,透過模式中的相關標記所匹配的資料值來替換?字元標記。替換的結果是一種完整的語句字元。重寫外掛要求伺服器解析它並執行重寫之後的語句將結果返回。

在新增和載入重寫規則後,檢查匹配規則模式的語句是否會被重寫:

mysql> select pi();
+----------+
| pi() |
+----------+
| 3.141593 |
+----------+
1 row in set (0.02 sec)
mysql> select 10;
+------+
| 10+1 |
+------+
| 11 |
+------+
1 row in set, 1 warning (0.00 sec)

從上面的執行結果來看,第一個查詢語句沒有出現重寫,但第二個查詢被重寫了。因為第二個查詢語句Rewriter外掛重寫語句後生成了一個警告資訊。為了檢視這個警告資訊可以使用show warnings:

mysql> show warnings\g
+-------+------+------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------------------------------------+
| Note | 1105 | Query 'select 10' rewritten to 'select 10+1' by a query rewrite plugin |
+-------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

為了啟用或禁用現有的規則,可以透過修改enabled列並重新載入規則表到重寫外掛。

為了禁用規則1

mysql> update query_rewrite.rewrite_rules set enabled='NO' where id=1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: NO
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)
mysql> select 10;
+----+
| 10 |
+----+
| 10 |
+----+
1 row in set (0.00 sec)

這就可以不同從表中刪除重寫規則來禁用重寫規則。

為了重新啟用重寫規則1:

mysql> update query_rewrite.rewrite_rules set enabled='YES' where id=1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected (0.02 sec)
mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
1 row in set (0.00 sec)
mysql> select 10;
+------+
| 10+1 |
+------+
| 11 |
+------+
1 row in set, 1 warning (0.00 sec)

rewrite_rules表包含了一個pattern_database列它是Rewriter用來匹配沒有使用資料庫名限定的表名:
.如果相關的資料庫和表名相同,語句中限定表名匹配模式中的限定名。
.只有預設資料庫與pattern_database一樣並且表名相同時語句中的非限定表名匹配模式中的非限定名

假設表mysql.cs有一個名為id的列並且應用程式從以下形式的查詢中選擇一個來從表中查詢記錄,這裡第二個查詢只能在預設資料庫為mysql的情況下被執行:
select * from mysql.cs where id=id_value;
select * from cs where id=id_value;

現在假設id列被重新命名為user_id了。這種修改意味著應用程式必須引用user_id而不是id。但是如果舊的應用程式不能進行修改,那麼它們將不能工作了。Rewriter重寫外掛可以解決這個問題。為了匹配和重寫那些不管是否有限定名的查詢語句,新增以下兩個規則並重新載入規則表:

mysql> select * from cs where mysql.id=1;
ERROR 1054 (42S22): Unknown column 'mysql.id' in 'where clause'
mysql> insert into query_rewrite.rewrite_rules(pattern,replacement)
-> values('select * from mysql.cs where id= ?','select * from mysql.cs where user_id= ?');
Query OK, 1 row affected (0.05 sec)
mysql> insert into query_rewrite.rewrite_rules(pattern,replacement,pattern_database)
-> values('select * from cs where id=?','select * from cs where user_id=?','mysql');
Query OK, 1 row affected (0.05 sec)
mysql> call query_rewrite.flush_rewrite_rules();
Query OK, 0 rows affected, 1 warning (0.02 sec)
mysql> select * from query_rewrite.rewrite_rules\G;
*************************** 1. row ***************************
id: 1
pattern: select ?
pattern_database: NULL
replacement: select ?+1
enabled: YES
message: NULL
pattern_digest: 3d4fc22e33e10d7235eced3c75a84c2c
normalized_pattern: select ?
*************************** 2. row ***************************
id: 2
pattern: select * from mysql.cs where id= ?
pattern_database: NULL
replacement: select * from mysql.cs where user_id= ?
enabled: YES
message: NULL
pattern_digest: 45281da14b71c1357dd053a4fe49dfac
normalized_pattern: select `*` from `mysql`.`cs` where (`id` = ?)
*************************** 3. row ***************************
id: 3
pattern: select * from cs where id=?
pattern_database: mysql
replacement: select * from cs where user_id=?
enabled: YES
message: NULL
pattern_digest: 0da2491bc4c0e1462cc020e4fcfde16b
normalized_pattern: select `*` from `mysql`.`cs` where (`id` = ?)
3 rows in set (0.00 sec)
mysql> select * from mysql.cs where id=1;
+------+------+---------+
| id | name | user_id |
+------+------+---------+
| 1 | jy | 1 |
+------+------+---------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
| Note | 1105 | Query 'select * from mysql.cs where id=1' rewritten to 'select * from mysql.cs where user_id= 1' by a query rewrite plugin |
+-------+------+----------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from cs where id=1;
+------+------+---------+
| id | name | user_id |
+------+------+---------+
| 1 | jy | 1 |
+------+------+---------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+---------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------------------------------------------------+
| Note | 1105 | Query 'select * from cs where id=1' rewritten to 'select * from cs where user_id=1' by a query rewrite plugin |
+-------+------+---------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Rewriter外掛使用第一個規則匹配有限定名的查詢,使用第二個規則匹配沒有限定名,但預設資料庫必須為mysql才能進行查詢重寫。

如何進行模式匹配
Rewriter外掛使用語句摘要和摘要雜湊值來使用重寫規則匹配輸入語句。max_digest_length系統變數決定了用於計算語句摘要的buffer大小。值越大計算的摘要越能區分更長的語句。值越小使用的記憶體越小但增加了更長語句使用相同摘要的可能性。

mysql> show global variables like 'max_digest_length';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| max_digest_length | 1024 |
+-------------------+-------+
1 row in set (0.00 sec)

外掛匹配語句進行重寫的規則如下:
1.計算語句摘要的雜湊值並將它與規則摘要雜湊值進行比較。這可能會出現誤報,但可以作為快速的拒絕測試

2.如果語句摘要雜湊值匹配任何一個模式摘要雜湊值,則匹配規範化雜湊值(語句摘要)將語句的形式轉化為匹配規則模式所規範化的形式。

3.如果規範化語句與規則匹配,請比較語句和模式中的文字值。模式中的一個?號值與語句中的任何文字值匹配。如果語句準備了一個SELECT語句,模式中的?也匹配語句中的?。否則,對應的文字必須相同。

如果多個規則匹配一個語句,則不確定外掛使用哪個規則來重寫該語句。

如果一個模式包含比替換更多的標記,那麼外掛將丟棄多餘的資料值。如果一個模式包含的標記比替換的少,這就是一個錯誤。當載入規則表時,外掛會注意到這一點,它會向規則行的message列寫入一條錯誤訊息來傳遞問題,並將Rewriter_reload_error狀態變數設定為ON。

重寫預備語句
預備語句是在解析時被重寫,而不是在執行時被重寫。預備語句與非預備語句的區別在於它們可能包含?字元作為引數標記。為了匹配預備語句中的?字元,重寫模式必須在同一個地方包含?字元。假設重寫規則具有這種模式
select ?, 3
下面列出了幾種預備語句和是否與它匹配的模式
預備語句                                模式是否匹配語句
prepare s as 'select 3, 3'     Yes
prepare s as 'select ?, 3'     Yes
prepare s as 'select 3, ?'     No
prepare s as 'select ?, ?'     No

Rewriter外掛操作資訊
Rewriter外掛透過幾種狀態變數來表示它的操作資訊:

mysql> show global status like 'Rewriter%';
+-----------------------------------+-------+
| Variable_name | Value |
+-----------------------------------+-------+
| Rewriter_number_loaded_rules | 3 |
| Rewriter_number_reloads | 5 |
| Rewriter_number_rewritten_queries | 5 |
| Rewriter_reload_error | OFF |
+-----------------------------------+-------+
4 rows in set (0.01 sec)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-2685033/,如需轉載,請註明出處,否則將追究法律責任。

相關文章