Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Using the Rewriter Query Rewrite Plugin

7.6.4.2 使用Rewriter查询重写插件

要启用或禁用插件,请启用或禁用 rewriter_enabled 系统变量。默认情况下,在安装插件时(见 第 7.6.4.1 节,“安装或卸载Rewriter查询重写插件”)时,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)。这可以用于控制语句和应该不变地复制的语句,例如来自 SOURCE_USER 的语句,指定了 CHANGE REPLICATION SOURCE TO。这也适用于 MySQL 客户端程序执行的语句,包括 mysqlbinlogmysqladminmysqldumpmysqlpump;因此,您应该授予 SKIP_QUERY_REWRITE 权限给这些实用程序使用的用户帐户。

添加重写规则

要添加 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();
Tip

如果您的重写规则似乎不起作用,请确保您已经重新加载了规则表 bằng调用 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 表中的行,以查看哪些问题存在。

模式使用与预备语句相同的语法(参阅 第 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 语句,以及规则模式是否匹配它们。

Prepared Statement Whether Pattern Matches Statement
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 表中的行,以查看哪些问题存在非 NULL message 列值。

Rewriter 插件的字符集使用

rewrite_rules 表加载到 Rewriter 插件时,插件使用当前全局值的 character_set_client 系统变量来解释语句。如果全局 character_set_client 值随后更改,则必须重新加载规则表。

客户端必须具有与全局值相同的会话 character_set_client 值,否则规则匹配将不适用于该客户端。