MySQL 8.0 リファレンスマニュアル


5.6.4.2 リライタクエリーリライトプラグインの使用

プラグインを有効または無効にするには、rewriter_enabled システム変数を有効または無効にします。 デフォルトでは、Rewriter プラグインはインストール時に有効になります (セクション5.6.4.1「リライタのクエリーリライトプラグインのインストールまたはアンインストール」 を参照)。 プラグインの初期状態を明示的に設定するには、サーバーの起動時に変数を設定します。 たとえば、オプションファイルでプラグインを有効にするには、次の行を使用します:

[mysqld]
rewriter_enabled=ON

実行時にプラグインを有効または無効にすることもできます:

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

Rewriter プラグインが有効になっていると仮定すると、サーバーによって受信されたリライト可能な各ステートメントを調査し、場合によっては変更します。 プラグインは、query_rewrite データベースの rewrite_rules テーブルからロードされるリライトルールのインメモリーキャッシュに基づいてステートメントをリライトするかどうかを決定します。

次のステートメントはリライトの対象となります:

  • MySQL 8.0.12 の時点: SELECT, INSERT, REPLACE, UPDATE および DELETE

  • MySQL 8.0.12 より前: SELECT only.

スタンドアロンステートメントおよびプリペアドステートメントはリライトの対象となります。 ビュー定義またはストアドプログラム内で発生するステートメントは、書き換えの対象にはなりません。

リライトルールの追加

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_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: d1b44b0c19af710b5a679907e284acd2ddc285201794bc69a2389d77baedddae
normalized_pattern: select ?

ステートメントダイジェスト、正規化されたステートメント、およびダイジェストハッシュ値については、セクション27.10「パフォーマンススキーマのステートメントダイジェストとサンプリング」 を参照してください。

なんらかのエラーが原因でルールをロードできない場合、flush_rewrite_rules() をコールするとエラーが発生します:

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

これが発生すると、プラグインはルール行の message カラムにエラーメッセージを書き込み、問題を伝達します。 NULL message 以外のカラム値を持つ行の rewrite_rules テーブルをチェックして、どのような問題が存在するかを確認します。

パターンは、プリペアドステートメントと同じ構文を使用します (セクション13.5.1「PREPARE ステートメント」 を参照)。 パターンテンプレート内では、? 文字はデータ値と一致するパラメータマーカーとして機能します。 パラメータマーカーは、SQL キーワードや識別子などではなく、データ値を指定するべき場所にしか使用できません。 ? 文字を引用符で囲まないでください。

パターンと同様に、置換には ? 文字を含めることができます。 パターンテンプレートに一致するステートメントの場合、プラグインはそれを書き換え、置換内の ? パラメータマーカーを、パターン内の対応するマーカーに一致するデータ値を使用して置き換えます。 結果は完全なステートメントの文字列になります。 プラグインはサーバーに解析を要求し、書き換えられたステートメントの表現として結果をサーバーに返します。

ルールを追加してロードした後、ステートメントがルールパターンと一致するかどうかに応じてリライトが行われるかどうかを確認します:

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 ステートメントではリライトは行われませんが、次のステートメントでは行われます。 2 番目のステートメントは、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 テーブルには、Rewriter がデータベース名で修飾されていないテーブル名の照合に使用する pattern_database カラムが含まれています:

  • ステートメントの修飾テーブル名は、対応するデータベース名とテーブル名が同一の場合、パターンの修飾名と一致します。

  • ステートメント内の修飾されていないテーブル名は、デフォルトのデータベースが pattern_database と同じで、テーブル名が同一の場合にのみ、パターン内の修飾されていない名前と一致します。

appdb.users という名前のテーブルに id という名前のカラムがあり、アプリケーションが次のいずれかの形式のクエリーを使用してテーブルから行を選択するとします。2 番目の形式は、appdb がデフォルトデータベースの場合にのみ使用できます:

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

また、id カラムの名前が user_id に変更されたとします (場合によっては、テーブルを変更して別のタイプの ID を追加する必要があり、id カラムが表す ID のタイプをより具体的に指定する必要があります)。

この変更は、アプリケーションが WHERE 句で id ではなく user_id を参照する必要があることを意味します。 ただし、生成される SELECT クエリーを変更するために書き込めない古いアプリケーションがある場合は、正しく機能しなくなります。 Rewriter プラグインはこの問題を解決できます。 テーブル名を修飾するかどうかに関係なくステートメントを照合およびリライトするには、次の 2 つのルールを追加してルールテーブルをリロードします:

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 (pattern_database の値) の場合のみ、秒を使用して、修飾されていない名前を使用したステートメントを照合します。

ステートメント照合の仕組み

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 プラグインは、いくつかのステータス変数を使用して、その操作に関する情報を使用可能にします:

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    |
+-----------------------------------+-------+

これらの変数の説明については、セクション5.6.4.3.4「リライタのクエリーリライトプラグインステータス変数」を参照してください。

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 テーブルで NULL 以外の message カラム値を持つ行をチェックして、どのような問題が存在するかを確認します。

リライタプラグインでの文字セットの使用

rewrite_rules テーブルが Rewriter プラグインにロードされると、プラグインは character_set_client システム変数の現在のグローバル値を使用してステートメントを解釈します。 グローバル character_set_client 値が後で変更された場合、ルールテーブルをリロードする必要があります。

クライアントは、ルールテーブルがロードされたときのグローバル値と同一のセッション character_set_client 値を持つ必要があります。そうしないと、そのクライアントに対してルール照合が機能しません。


関連キーワード:  ステートメント, rewrite, ルール, テーブル, rules, サーバー, 変数, Rewriter, パターン, リライト