文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手冊頁 (TGZ) - 258.5Kb
手冊頁 (Zip) - 365.5Kb
資訊 (Gzip) - 4.0Mb
資訊 (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  使用 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

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

執行具有 SKIP_QUERY_REWRITE 權限的使用者所執行的陳述式不會受到重寫的影響,前提是 rewriter_enabled_for_threads_without_privilege_checks 系統變數設定為 OFF(預設為 ON)。這可用於控制陳述式和應保持不變複製的陳述式,例如 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 節,「Performance Schema 陳述式摘要和取樣」

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

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

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

模式使用與預備陳述式相同的語法(請參閱第 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 資料表,查看是否有 message 資料行值為非 NULL 的資料列,以查看存在哪些問題。

Rewriter 外掛程式字元集的使用

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

用戶端的 character_set_client 值必須與規則表載入時的全域值相同,否則該用戶端的規則匹配將無法運作。