文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  ...  /  使用 Rewriter 查詢重寫外掛程式

7.6.4.2 使用 Rewriter 查詢重寫外掛程式

若要啟用或停用外掛程式,請啟用或停用 rewriter_enabled 系統變數。依預設,當您安裝 Rewriter 外掛程式時,它會啟用 (請參閱第 7.6.4.1 節,「安裝或解除安裝 Rewriter 查詢重寫外掛程式」)。若要明確設定初始外掛程式狀態,您可以在伺服器啟動時設定變數。例如,若要在選項檔案中啟用外掛程式,請使用以下幾行

[mysqld]
rewriter_enabled=ON

也可以在執行階段啟用或停用外掛程式

SET GLOBAL rewriter_enabled = ON;
SET GLOBAL rewriter_enabled = OFF;

假設已啟用 Rewriter 外掛程式,它會檢查並可能修改伺服器收到的每個可重寫陳述式。外掛程式會根據其記憶體中快取的重寫規則來判斷是否重寫陳述式,這些規則是從 query_rewrite 資料庫中的 rewrite_rules 表格載入。

以下陳述式會受到重寫:SELECTINSERTREPLACEUPDATEDELETE

獨立陳述式和預備陳述式會受到重寫。在檢視定義或儲存程式內發生的陳述式不會受到重寫。

如果 rewriter_enabled_for_threads_without_privilege_checks 系統變數設定為 OFF (預設值為 ON),則具有 SKIP_QUERY_REWRITE 權限的使用者執行的陳述式不會受到重寫。這可用於控制陳述式和應保持不變複製的陳述式,例如 CHANGE REPLICATION SOURCE TO 所指定的 SOURCE_USER 中的陳述式。這也適用於 MySQL 用戶端程式執行的陳述式,包括 mysqlbinlogmysqladminmysqldump;因此,您應該將 SKIP_QUERY_REWRITE 授予這些公用程式用來連線到 MySQL 的使用者帳戶。

新增重寫規則

若要為 Rewriter 外掛程式新增規則,請將資料列新增至 rewrite_rules 表格,然後叫用 flush_rewrite_rules() 儲存程序,將規則從表格載入至外掛程式。下列範例會建立簡單規則,以比對選取單一常值值的陳述式

INSERT INTO query_rewrite.rewrite_rules (pattern, replacement)
VALUES('SELECT ?', 'SELECT ? + 1');

產生的表格內容如下

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

規則指定一個模式範本,指示要比對哪些 SELECT 陳述式,以及一個取代範本,指示如何重寫比對的陳述式。但是,僅將規則新增至 rewrite_rules 表格不足以使 Rewriter 外掛程式使用該規則。您必須叫用 flush_rewrite_rules(),將表格內容載入至外掛程式記憶體中快取

mysql> CALL query_rewrite.flush_rewrite_rules();
提示

如果您的重寫規則似乎無法正常運作,請確保您已呼叫 flush_rewrite_rules() 重新載入規則表。

當外掛程式從規則表中讀取每條規則時,它會從模式計算出正規化(語句摘要)形式和摘要雜湊值,並使用它們來更新 normalized_patternpattern_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: d1b44b0c19af710b5a679907e284acd2ddc285201794bc69a2389d77baedddae
normalized_pattern: select ?

有關語句摘要、正規化語句和摘要雜湊值的資訊,請參閱第 29.10 節「效能綱要語句摘要和取樣」

如果由於某些錯誤而無法載入規則,呼叫 flush_rewrite_rules() 會產生錯誤。

mysql> CALL query_rewrite.flush_rewrite_rules();
ERROR 1644 (45000): Loading of some rule(s) failed.

發生這種情況時,外掛程式會將錯誤訊息寫入規則列的 message 資料行,以傳達問題。檢查 rewrite_rules 表中具有非 NULLmessage 資料行值的列,以查看存在哪些問題。

模式使用與預先處理語句相同的語法(請參閱第 15.5.1 節「PREPARE 陳述式」)。在模式範本中,? 字元充當與資料值比對的參數標記。 ? 字元不應包含在引號內。參數標記只能在應顯示資料值的位置使用,而不能用於 SQL 關鍵字、識別碼、函式等。外掛程式會剖析語句以識別文字值(如第 11.1 節「文字值」中所定義),因此您可以在任何文字值的位置放置參數標記。

與模式一樣,取代也可以包含 ? 字元。對於符合模式範本的語句,外掛程式會重寫它,使用模式中對應的標記比對的資料值取代取代中的 ? 參數標記。結果是一個完整的語句字串。外掛程式會要求伺服器剖析它,並將結果作為重寫語句的表示形式傳回伺服器。

在新增並載入規則後,請檢查是否根據語句是否符合規則模式進行重寫。

mysql> SELECT PI();
+----------+
| PI()     |
+----------+
| 3.141593 |
+----------+
1 row in set (0.01 sec)

mysql> SELECT 10;
+--------+
| 10 + 1 |
+--------+
|     11 |
+--------+
1 row in set, 1 warning (0.00 sec)

第一個 SELECT 語句不會發生重寫,但第二個語句會發生。第二個語句說明當 Rewriter 外掛程式重寫語句時,會產生警告訊息。若要檢視訊息,請使用 SHOW WARNINGS

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1105
Message: Query 'SELECT 10' rewritten to 'SELECT 10 + 1' by a query rewrite plugin

語句不需要重寫為相同類型的語句。以下範例載入一個規則,該規則將 DELETE 語句重寫為 UPDATE 語句。

INSERT INTO query_rewrite.rewrite_rules (pattern, replacement)
VALUES('DELETE FROM db1.t1 WHERE col = ?',
       'UPDATE db1.t1 SET col = NULL WHERE col = ?');
CALL query_rewrite.flush_rewrite_rules();

若要啟用或停用現有規則,請修改其 enabled 資料行,並將表格重新載入外掛程式。若要停用規則 1

UPDATE query_rewrite.rewrite_rules SET enabled = 'NO' WHERE id = 1;
CALL query_rewrite.flush_rewrite_rules();

這可讓您停用規則,而無需將其從表格中移除。

若要重新啟用規則 1

UPDATE query_rewrite.rewrite_rules SET enabled = 'YES' WHERE id = 1;
CALL query_rewrite.flush_rewrite_rules();

rewrite_rules 表格包含 pattern_database 資料行,Rewriter 使用此資料行來比對未以資料庫名稱限定的表格名稱。

  • 如果對應的資料庫和表格名稱相同,則語句中的限定表格名稱會比對模式中的限定名稱。

  • 只有在預設資料庫與 pattern_database 相同且表格名稱相同時,語句中的非限定表格名稱才會比對模式中的非限定名稱。

假設名為 appdb.users 的表格有一個名為 id 的資料行,並且預期應用程式會使用以下其中一種形式的查詢從表格中選取資料列,其中第二種形式可以在 appdb 為預設資料庫時使用。

SELECT * FROM users WHERE appdb.id = id_value;
SELECT * FROM users WHERE id = id_value;

假設 id 資料行也重新命名為 user_id(可能必須修改表格以新增另一種 ID 類型,而且有必要更明確地指出 id 資料行所代表的 ID 類型)。

此變更表示應用程式必須在 WHERE 子句中參照 user_id,而不是 id,但無法更新的舊應用程式將無法正常運作。Rewriter 外掛程式可以透過比對和重寫有問題的語句來解決此問題。若要比對語句 SELECT * FROM appdb.users WHERE id = value,並將其重寫為 SELECT * FROM appdb.users WHERE user_id = value,您可以將表示取代規則的列插入重寫規則表格中。如果您也想要使用非限定表格名稱來比對此 SELECT,則也必須新增明確的規則。使用 ? 作為值預留位置,所需的兩個 INSERT 語句如下所示

INSERT INTO query_rewrite.rewrite_rules
    (pattern, replacement) VALUES(
    'SELECT * FROM appdb.users WHERE id = ?',
    'SELECT * FROM appdb.users WHERE user_id = ?'
    );
INSERT INTO query_rewrite.rewrite_rules
    (pattern, replacement, pattern_database) VALUES(
    'SELECT * FROM users WHERE id = ?',
    'SELECT * FROM users WHERE user_id = ?',
    'appdb'
    );

在新增兩個新規則後,執行以下語句以使其生效

CALL query_rewrite.flush_rewrite_rules();

Rewriter 使用第一個規則來比對使用限定表格名稱的語句,並使用第二個規則來比對使用非限定名稱的語句。第二個規則僅在 appdb 是預設資料庫時才有效。

語句比對的運作方式

Rewriter 外掛程式使用語句摘要和摘要雜湊值,分階段將傳入的語句與重寫規則比對。max_digest_length 系統變數決定用於計算語句摘要的緩衝區大小。較大的值可計算出區分較長語句的摘要。較小的值會使用較少的記憶體,但會增加較長語句與相同摘要值衝突的可能性。

外掛程式會將每個語句與重寫規則進行比對,如下所示

  1. 計算語句摘要雜湊值,並將其與規則摘要雜湊值進行比較。這容易出現誤判,但可用作快速拒絕測試。

  2. 如果語句摘要雜湊值與任何模式摘要雜湊值比對,則將語句的正規化(語句摘要)形式與比對規則模式的正規化形式比對。

  3. 如果正規化語句符合規則,則比較語句和模式中的文字值。模式中的 ? 字元會比對語句中的任何文字值。如果語句預先處理語句,則模式中的 ? 也會比對語句中的 ?。否則,對應的文字必須相同。

如果多個規則符合語句,則外掛程式使用哪個規則來重寫語句是不確定的。

如果模式包含的標記多於取代,則外掛程式會捨棄多餘的資料值。如果模式包含的標記少於取代,則會產生錯誤。外掛程式會在載入規則表時注意到這一點,並將錯誤訊息寫入規則列的 message 資料行以傳達問題,並將 Rewriter_reload_error 狀態變數設為 ON

重寫預先處理語句

預先處理語句會在剖析時重寫(也就是說,在預先處理時重寫),而不是在稍後執行時重寫。

預先處理語句與非預先處理語句的不同之處在於,它們可能包含 ? 字元作為參數標記。若要比對預先處理語句中的 ?Rewriter 模式必須在相同位置包含 ?。假設重寫規則具有此模式

SELECT ?, 3

下表顯示數個預先處理的 SELECT 語句,以及規則模式是否符合這些語句。

預先處理語句 模式是否符合語句
PREPARE s AS 'SELECT 3, 3'
PREPARE s AS 'SELECT ?, 3'
PREPARE s AS 'SELECT 3, ?'
PREPARE s AS 'SELECT ?, ?'
Rewriter 外掛程式的運作資訊

Rewriter 外掛程式透過數個狀態變數提供有關其運作的資訊

mysql> SHOW GLOBAL STATUS LIKE 'Rewriter%';
+-----------------------------------+-------+
| Variable_name                     | Value |
+-----------------------------------+-------+
| Rewriter_number_loaded_rules      | 1     |
| Rewriter_number_reloads           | 5     |
| Rewriter_number_rewritten_queries | 1     |
| Rewriter_reload_error             | ON    |
+-----------------------------------+-------+

如需這些變數的描述,請參閱第 7.6.4.3.4 節「Rewriter 查詢重寫外掛程式狀態變數」

當您呼叫 flush_rewrite_rules() 預存程序來載入規則表時,如果某些規則發生錯誤,則 CALL 語句會產生錯誤,且外掛程式會將 Rewriter_reload_error 狀態變數設為 ON

mysql> CALL query_rewrite.flush_rewrite_rules();
ERROR 1644 (45000): Loading of some rule(s) failed.

mysql> SHOW GLOBAL STATUS LIKE 'Rewriter_reload_error';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Rewriter_reload_error | ON    |
+-----------------------+-------+

在這種情況下,請檢查 rewrite_rules 表中具有非 NULLmessage 資料行值的列,以查看存在哪些問題。

Rewriter 外掛程式的字元集使用

rewrite_rules 表格載入 Rewriter 外掛程式時,外掛程式會使用 character_set_client 系統變數的目前全域值來解譯語句。如果後續變更全域 character_set_client 值,則必須重新載入規則表。

用戶端必須具有與載入規則表時全域值相同的會期 character_set_client 值,否則規則比對將不適用於該用戶端。