要启用或禁用插件,请启用或禁用 rewriter_enabled
系统变量。默认情况下,Rewriter
插件在安装时处于启用状态(请参阅 第 7.6.4.1 节,“安装或卸载 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
)。这可用于控制语句和应保持不变的语句,例如 CHANGE REPLICATION SOURCE TO
指定的 SOURCE_USER
中的语句。这也适用于由 MySQL 客户端程序(包括 mysqlbinlog、mysqladmin 和 mysqldump)执行的语句;因此,您应将 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_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
表,查看 message
列值不为 NULL
的行,以了解存在哪些问题。
模式使用与预处理语句相同的语法(参见 第 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
语句,以及规则模式是否与它们匹配。
预处理语句 | 模式是否与语句匹配 |
---|---|
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
表,查看 message
列值不为 NULL
的行,以了解存在哪些问题。
当 rewrite_rules
表加载到 Rewriter
插件中时,插件会使用 character_set_client
系统变量的当前全局值来解释语句。如果随后更改了全局 character_set_client
值,则必须重新加载规则表。
客户端必须具有与加载规则表时全局值相同的会话 character_set_client
值,否则该客户端的规则匹配将无法正常工作。