要启用或禁用插件,请启用或禁用 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
表中加载。
以下语句可能会被重写:SELECT
、INSERT
、REPLACE
、UPDATE
和 DELETE
。
独立语句和预备语句都可能被重写。视图定义或存储程序中的语句不可能被重写。
具有 SKIP_QUERY_REWRITE
权限的用户执行的语句不可能被重写,前提是 rewriter_enabled_for_threads_without_privilege_checks
系统变量设置为 OFF
(默认 ON
)。这可以用于控制语句和应该不变地复制的语句,例如来自 SOURCE_USER
的语句,指定了 CHANGE REPLICATION SOURCE TO
。这也适用于 MySQL 客户端程序执行的语句,包括 mysqlbinlog、mysqladmin、mysqldump 和 mysqlpump;因此,您应该授予 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();
如果您的重写规则似乎不起作用,请确保您已经重新加载了规则表 bằng调用 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 ?
有关语句摘要、标准化语句和摘要哈希值的信息,请参阅 第 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
系统变量确定用于计算语句摘要的缓冲区大小。较大的值使得可以计算更长语句的摘要,较小的值使用较少的内存,但增加了较长语句碰撞到相同摘要值的可能性。
插件按照以下步骤匹配每个语句到重写规则:
-
计算语句摘要哈希值,并将其与规则摘要哈希值进行比较。这可能会出现假阳性,但可以作为快速排除测试。
-
如果语句摘要哈希值与任何模式摘要哈希值匹配,则将语句的标准化(语句摘要)形式与匹配规则模式的标准化形式进行匹配。
-
如果标准化语句匹配规则,则比较语句和模式中的文字值。
?
字符在模式中匹配语句中的任何文字值。如果语句准备语句,?
在模式中也匹配语句中的?
。否则,相应的文字值必须相同。
如果多个规则匹配语句,则插件使用哪个规则来重写语句是不可确定的。
如果模式包含更多标记符号,而替换则少,插件将丢弃多余的数据值。如果模式包含较少的标记符号,而替换则多,则这是一个错误。插件在加载规则表时注意到这个问题,写入错误消息到规则行的 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
插件通过多个状态变量提供其操作信息:
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
列值。
当 rewrite_rules
表加载到 Rewriter
插件时,插件使用当前全局值的 character_set_client
系统变量来解释语句。如果全局 character_set_client
值随后更改,则必须重新加载规则表。
客户端必须具有与全局值相同的会话 character_set_client
值,否则规则匹配将不适用于该客户端。