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
数据库中。
以下语句适用于重写: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();
如果您的重写规则似乎不工作,请确保您已经重新加载了规则表 bằng调用flush_rewrite_rules()
。
当插件读取每个规则时,它将计算模式和摘要值的 normalized (statement digest) 表单,并使用它们来更新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 ?
有关语句摘要、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 =
,可以插入一个表示替换规则的行到重写规则表中。如果你还想匹配使用未qualified表名的SELECT语句,需要添加明确的规则。使用value
?
作为值占位符,两个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规则进行匹配如下:
-
计算语句摘要哈希值,并将其与规则摘要哈希值进行比较。这可能会出现false positives,但作为快速拒绝测试。
-
如果语句摘要哈希值匹配任何模式摘要哈希值,匹配normalized(语句摘要)形式的语句到规则模式的normalized形式。
-
如果语句匹配规则,比较语句中的文字值和模式中的文字值。
?
在模式中匹配语句中的任何文字值。如果语句准备语句,?
在模式中也匹配语句中的?
。否则,相应的文字值必须相同。
如果多个规则匹配语句,它是非确定的哪个规则插件使用以重写语句。
如果模式包含更多的标记符号,而不是替换,它插件将丢弃超出数据值。如果模式包含更少的标记符号,而不是替换,它是一个错误。插件在加载规则表时注意到这个问题,写入错误消息到规则行的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
值,以便规则匹配工作。