Documentation Home

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插件已启用,它会检查并可能修改服务器收到的每个可重写语句。该插件根据其在内存中缓存的重写规则来确定是否重写语句,这些重写规则是从数据库rewrite_rules中的表加载的query_rewrite

这些陈述可能会被重写:

独立语句和准备好的语句可能会被重写。视图定义或存储程序中出现的语句不会被重写。

从 MySQL 8.0.31 开始,具有 SKIP_QUERY_REWRITE权限的用户运行的语句不会被重写,前提是 rewriter_enabled_for_threads_without_privilege_checks 系统变量设置为OFF(default ON)。这可用于控制语句和应原样复制的语句,例如来自SOURCE_USER指定的 语句CHANGE REPLICATION SOURCE TO。对于 MySQL 客户端程序执行的语句也是如此,包括mysqlbinlogmysqladminmysqldumpmysqlpump;出于这个原因,你应该授予 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 ?

有关语句摘要、规范化语句和摘要哈希值的信息,请参阅 第 27.10 节,“性能模式语句摘要和采样”

如果由于某些错误而无法加载规则,则调用 flush_rewrite_rules()会产生错误:

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

发生这种情况时,插件会将错误消息写入 message规则行的列以传达问题。检查 rewrite_rules表中具有非NULL message列值的行以查看存在哪些问题。

模式使用与准备好的语句相同的语法(请参阅 第 13.5.1 节,“PREPARE 语句”)。在模式模板中, ?字符充当匹配数据值的参数标记。?字符不应包含在引号内。参数标记只能用于数据值应该出现的地方,不能用于 SQL 关键字、标识符、函数等。该插件解析语句以识别文字值(如第 9.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

不需要将语句重写为相同类型的语句。以下示例加载了一个将语句重写 DELETEUPDATE语句的规则:

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且表名相同时,语句中的非限定表名才匹配模式中的非限定名。

假设一个名为 name 的表appdb.users有一个名为 named 的列id,并且应用程序应该使用这些形式之一的查询从表中选择行,其中第二种可以 appdb在默认数据库时使用:

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

还假设该id列重命名为user_id(可能必须修改表以添加另一种类型的 ID,并且有必要更具体地指示该 id列代表什么类型的 ID)。

更改意味着应用程序必须引用 user_id而不是id 在该WHERE条款中,但无法更新的旧应用程序将无法正常工作。该 Rewriter插件可以通过匹配和重写有问题的语句来解决这个问题。要匹配语句并将其重写为 ,您可以将表示替换规则的行插入到重写规则表中。如果你也想 使用非限定表名来匹配这个,也需要添加显式规则。作为值占位符,所需的两个 语句 如下所示: SELECT * FROM appdb.users WHERE id = valueSELECT * FROM appdb.users WHERE user_id = valueSELECT?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

重写准备好的语句

准备好的语句在解析时(即准备好时)重写,而不是在稍后执行时重写。

准备好的语句与非准备语句的不同之处在于它们可能包含?字符作为参数标记。要匹配?准备好的语句中的 a,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列值的行,以查看存在哪些问题。

Rewriter Plugin 字符集的使用

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

客户端的会话值必须 character_set_client与加载规则表时的全局值相同,否则规则匹配对该客户端不起作用。