Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


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

7.6.4.2 使用重写器查询重写插件

要启用或禁用插件,请启用或禁用rewriter_enabled系统变量。默认情况下,Rewriter插件在安装时启用(见第7.6.4.1节,“Installing or Uninstalling the Rewriter Query Rewrite Plugin”)。要在服务器启动时设置插件的初始状态,可以在选项文件中使用以下行:

[mysqld]
rewriter_enabled=ON

也可以在运行时启用或禁用插件:

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

假设Rewriter插件已启用,它将检查和可能修改每个可重写的语句,语句由服务器接收。插件确定是否重写语句基于其内存缓存中的重写规则,这些规则来自rewrite_rules表在query_rewrite数据库中。

以下语句适用于重写: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();
Tip

如果您的重写规则似乎不工作,请确保您已经重新加载了规则表 bằng调用flush_rewrite_rules()

当插件读取每个规则时,它将计算模式和摘要值的 normalized (statement digest) 表单,并使用它们来更新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 ?

有关语句摘要、normalized 语句和摘要哈希值的信息,请见第29.10节,“Performance Schema Statement Digests and Sampling”

如果规则无法加载由于某些错误,调用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列来匹配未qualified的表名:

  • qualified表名在语句中匹配pattern中的qualified名,如果对应的数据库和表名相同。

  • 未qualified表名在语句中匹配pattern中的未qualified名,只要默认数据库是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,可以插入一个表示替换规则的行到重写规则表中。如果你还想匹配使用未qualified表名的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使用第一个规则匹配使用qualified表名的语句,并使用第二个规则匹配使用unqualified名的语句。第二个规则仅在appdb是默认数据库时生效。

Rewriter插件使用语句摘要和摘要哈希值来匹配incoming语句对rewrite规则进行阶段匹配。系统变量max_digest_length确定了用于计算语句摘要的缓冲区大小。更大的值可以计算更长的语句摘要。较小的值使用更少的内存,但增加了长语句与同一摘要值的可能性。

插件将每个语句与rewrite规则进行匹配如下:

  1. 计算语句摘要哈希值,并将其与规则摘要哈希值进行比较。这可能会出现false positives,但作为快速拒绝测试。

  2. 如果语句摘要哈希值匹配任何模式摘要哈希值,匹配normalized(语句摘要)形式的语句到规则模式的normalized形式。

  3. 如果语句匹配规则,比较语句中的文字值和模式中的文字值。?在模式中匹配语句中的任何文字值。如果语句准备语句,?在模式中也匹配语句中的?。否则,相应的文字值必须相同。

如果多个规则匹配语句,它是非确定的哪个规则插件使用以重写语句。

如果模式包含更多的标记符号,而不是替换,它插件将丢弃超出数据值。如果模式包含更少的标记符号,而不是替换,它是一个错误。插件在加载规则表时注意到这个问题,写入错误消息到规则行的message列,以便通信问题,并将Rewriter_reload_error状态变量设置为ON

Rewriting 预处理语句

预处理语句将在解析时(即准备时)重写,而不是在后续执行时。

预编译语句与非预编译语句不同的是,它们可能包含?字符作为参数标记符。要在预编译语句中匹配?,rewrite规则中的Rewriter模式必须在同一位置包含?。假设rewrite规则具有以下模式:

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插件通过多个状态变量提供关于其操作的信息:

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 Query Rewrite Plugin Status Variables”

当您调用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表中的行,以查看存在的问题。

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

客户端必须具有与规则表加载时的全局character_set_client值相同的会话character_set_client值,以便规则匹配工作。