MySQL服务器重写器查询重写插件
MySQL支持查询重写插件,该插件可以在服务器执行SQL语句之前检查并可能修改服务器收到的SQL语句。请参阅查询重写插件。
MySQL发行版包括一个名为的后解析查询重写插件,Rewriter
以及用于安装该插件及其相关组件的脚本。这些组件协同工作以提供语句重写功能:
- 一个服务器端插件
Rewriter
,根据其内存中的重写规则缓存,可以检查语句并可以对其进行重写。 这些语句可以重写:
- 在MySQL 8.0.12的:
SELECT
,INSERT
,REPLACE
,UPDATE
,和DELETE
。 SELECT
仅适用于MySQL 8.0.12之前的版本。
独立语句和准备好的语句可能会被重写。在视图定义或存储程序中出现的语句无需重写。
- 在MySQL 8.0.12的:
- 该
Rewriter
插件使用名为的数据库,query_rewrite
其中包含名为的表rewrite_rules
。该表为插件用来决定是否重写语句的规则提供持久存储。用户通过修改存储在此表中的规则集来与插件进行通信。该插件通过设置message
表行的列与用户通信。 - 该
query_rewrite
数据库包含一个名为的存储过程flush_rewrite_rules()
,该存储过程将规则表的内容加载到插件中。 - 存储过程
load_rewrite_rules()
使用名为的用户定义函数flush_rewrite_rules()
。 - 该
Rewriter
插件公开了启用插件配置的系统变量和提供运行时操作信息的状态变量。
以下各节描述了如何安装和使用Rewriter
插件,并提供了其相关组件的参考信息。
安装或卸载重写器查询重写插件
注意如果已安装,则
Rewriter
即使禁用该插件也会带来一些开销。为了避免这种开销,除非计划使用该插件,否则不要安装该插件。
要安装或卸载Rewriter
查询重写插件,请选择位于share
MySQL安装目录中的相应脚本:
install_rewriter.sql
:选择此脚本来安装Rewriter
插件及其相关组件。uninstall_rewriter.sql
:选择此脚本来卸载Rewriter
插件及其相关组件。
运行选定的脚本,如下所示:
shell>mysql -u root -p < install_rewriter.sql Enter password: (enter root password here)
此处的示例使用install_rewriter.sql
安装脚本。uninstall_rewriter.sql
如果要卸载插件,请替换。
运行安装脚本应安装并启用插件。要验证这一点,请连接到服务器并执行以下语句:
mysql>SHOW GLOBAL VARIABLES LIKE 'rewriter_enabled'; +------------------ +------- + | Variable_name | Value | +------------------ +------- + | rewriter_enabled | ON | +------------------ +------- +
有关用法说明,有关参考信息,请参见“重写器查询重写插件参考”。
使用重写器查询重写插件
要启用或禁用插件,请启用或禁用rewriter_enabled
系统变量。默认情况下,Rewriter
插件在安装时处于启用状态(请参见“ddl_rewriter插件”)。要显式设置初始插件状态,可以在服务器启动时设置变量。例如,要在选项文件中启用插件,请使用以下几行:
[mysqld] rewriter_enabled=ON
也可以在运行时启用或禁用插件:
SET GLOBAL rewriter_enabled =ON ;SET GLOBAL rewriter_enabled = OFF;
假设Rewriter
插件已启用,它将检查并可能修改服务器收到的每个可重写语句。该插件根据其在内存中的重写规则高速缓存来确定是否重写语句,这些高速缓存是从数据库中的rewrite_rules
表加载的query_rewrite
。
这些语句可以重写:
- 在MySQL 8.0.12的:
SELECT
,INSERT
,REPLACE
,UPDATE
,和DELETE
。 SELECT
仅适用于MySQL 8.0.12之前的版本。
独立语句和准备好的语句可能会被重写。在视图定义或存储程序中出现的语句无需重写。
- 添加重写规则
- 语句匹配如何工作
- 重写准备好的语句
- 重写器插件操作信息
- 重写器插件对字符集的使用
添加重写规则
要为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 ?
有关语句摘要,规范化语句和摘要哈希值的信息,请参见“性能模式语句摘要和采样”。
如果由于某些错误而无法加载规则,则调用flush_rewrite_rules()
会产生错误:
mysql>CALL query_rewrite.flush_rewrite_rules(); ERROR 1644 (45000): Loading of some rule(s) failed.
发生这种情况时,插件会将错误消息写入message
规则行的列以传达问题。检查rewrite_rules
表中具有非NULL
message
列值的行,以参见存在什么问题。
模式使用与准备好的语句相同的语法(请参见“ PREPARE语句”)。在模式模板中,?
字符充当与数据值匹配的参数标记。参数标记只能用于应该显示数据值的位置,而不能用于SQL关键字,标识符等。该?
字符不应引号括起来。
与模式一样,替换内容可以包含?
字符。对于与模式模板匹配的语句,插件将对其进行重写,?
并使用与模式中相应标记匹配的数据值替换替换中的参数标记。结果是一个完整的语句字符串。插件要求服务器解析它,并将结果作为重写语句的表示形式返回给服务器。
添加和加载规则后,根据语句是否与规则模式匹配来检查是否发生重写:
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_rulesSET enabled = 'NO'WHERE id = 1;CALL query_rewrite.flush_rewrite_rules();
这使您可以停用规则而不将其从表中删除。
重新启用规则1:
UPDATE query_rewrite.rewrite_rulesSET enabled = 'YES'WHERE id = 1;CALL query_rewrite.flush_rewrite_rules();
该rewrite_rules
表包含一pattern_database
列,Rewriter
用于匹配不具有数据库名称限定的表名称:
- 如果相应的数据库名和表名相同,则语句中的合格表名与模式中的合格名匹配。
- 仅当默认数据库与默认数据库相同
pattern_database
且表名相同时,语句中不合格的表名才会与模式中的不合格名匹配。
假设名为表 ppdb.users
的列名为id
,并且应用程序应使用以下形式之一的查询从表中选择行,仅当 ppdb
默认数据库为第二种时,才可以使用第二种形式:
SELECT *FROM usersWHERE appdb.id = id_value;SELECT *FROM usersWHERE id = id_value;
还假设将id
列重命名为user_id
(也许必须修改表以添加另一种类型的ID,并且有必要更具体地指示该id
列代表的ID的类型)。
这种变化意味着应用程序必须引用user_id
,而不是id
对在WHERE
条款。但是,如果有些旧的应用程序无法编写以更改SELECT
它们生成的查询,则它们将不再正常运行。该Rewriter
插件可以解决此问题。要匹配和重写语句(无论它们是否符合表名),请添加以下两个规则并重新加载规则表:
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
使用第一条规则来匹配使用限定表名的语句。它使用秒数来匹配使用非限定名称的语句,但前提是默认数据库为 ppdb
(中的值pattern_database
)。
语句匹配如何工作
该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 | +----------------------------------- +------- +
有关这些变量的描述,请参见“重写器查询重写插件状态变量”。
通过调用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
值必须与加载规则表时的全局值相同,否则规则匹配将不适用于该客户端。
重写器查询重写插件参考
以下讨论可作为与Rewriter
查询重写插件关联的这些组件的参考:
- 数据库中的
Rewriter
规则表query_rewrite
Rewriter
程序和功能Rewriter
系统和状态变量
5.6.4.3.1重写器查询重写插件规则表
数据库中的rewrite_rules
表query_rewrite
为Rewriter
插件用来决定是否重写语句的规则提供持久性存储。
用户通过修改存储在此表中的规则集来与插件进行通信。该插件通过设置表的message
列将信息传达给用户。
注意规则表通过
flush_rewrite_rules
存储过程加载到插件中。除非在最近的表修改之后调用了该过程,否则表内容不一定与插件使用的规则集相对应。
该rewrite_rules
表包含以下列:
id
规则编号。此列是表的主键。您可以使用ID唯一地标识任何规则。
pattern
指示规则匹配的语句的模式的模板。使用
?
代表参数标记相匹配的数据值。pattern_database
该数据库用于匹配语句中不合格的表名。如果相应的数据库名和表名相同,则语句中的合格表名与模式中的合格名匹配。仅当默认数据库与默认数据库相同
pattern_database
且表名相同时,语句中不合格的表名才会与模式中的不合格名匹配。replacement
指示如何重写与
pattern
列值匹配的语句的模板。使用?
代表参数标记相匹配的数据值。在重写的语句中,插件使用与中的相应标记匹配的数据值替换?
参数标记。replacement
pattern
enabled
是否启用规则。加载操作(通过调用
flush_rewrite_rules()
存储过程执行)Rewriter
仅在此列为时将规则从表中加载到内存中的缓存中YES
。通过此列,可以在不删除规则的情况下停用规则:将列设置为以外的值
YES
,然后将表重新加载到插件中。message
插件使用此列与用户交流。如果将规则表加载到内存中时未发生错误,则插件会将
message
列设置为NULL
。非NULL
值表示错误,并且列内容为错误消息。在以下情况下可能会发生错误:- 模式或替换是产生语法错误的错误SQL语句。
- 替换包含的
?
参数标记多于模式。
如果发生加载错误,则插件还将
Rewriter_reload_error
状态变量设置为ON
。pattern_digest
此列用于调试和诊断。如果在将规则表加载到内存时该列存在,则插件将使用模式摘要对其进行更新。如果您尝试确定为什么某些语句无法重写的情况,那么此列可能会很有用。
normalized_pattern
此列用于调试和诊断。如果在将规则表加载到内存时该列存在,则插件将使用模式的规范化形式对其进行更新。如果您尝试确定为什么某些语句无法重写的情况,那么此列可能会很有用。
5.6.4.3.2重写器查询重写插件的过程和功能
Rewriter
插件操作使用将规则表加载到其内存缓存中的存储过程以及帮助程序用户定义函数(UDF)。在正常操作下,用户仅调用存储过程。UDF旨在由存储过程而不是由用户直接调用。
flush_rewrite_rules()
此存储过程使用
load_rewrite_rules()
UDF将rewrite_rules
表的内容加载到Rewriter
内存中的缓存中。调用
flush_rewrite_rules()
意味着COMMIT
。修改rules表以使插件从新表内容更新其缓存后,调用此过程。如果发生任何错误,则插件会将
message
表中相应规则行的列设置为,并将Rewriter_reload_error
status变量设置为ON
。load_rewrite_rules()
此UDF是
flush_rewrite_rules()
存储过程使用的帮助程序。
5.6.4.3.3重写器查询重写插件系统变量
该Rewriter
查询重写插件支持以下系统变量。只有安装了插件,这些变量才可用(请)。
rewriter_enabled
属性 值 系统变量 rewriter_enabled
范围 全球 动态 是 SET_VAR
提示适用没有 类型 布尔型 默认值 ON
无论是
Rewriter
查询重写插件启用。rewriter_verbose
属性 值 系统变量 rewriter_verbose
范围 全球 动态 是 SET_VAR
提示适用没有 类型 整数 供内部使用。
5.6.4.3.4重写器查询重写插件状态变量
该Rewriter
查询重写插件支持以下状态变量。只有安装了插件,这些变量才可用(请)。
Rewriter_number_loaded_rules
从
rewrite_rules
表成功加载到内存以供Rewriter
插件使用的重写插件重写规则的数量。Rewriter_number_reloads
次数的
rewrite_rules
表已经被加载到由所使用的内存中缓存Rewriter
插件。Rewriter_number_rewritten_queries
Rewriter
自从查询重写插件加载以来被重写的查询数。Rewriter_reload_error
rewrite_rules
表最近一次被加载到Rewriter
插件使用的内存缓存中时是否发生错误。如果值为OFF
,则不会发生错误。如果值为,则ON
发生错误;检查表的message
列中rewriter_rules
是否有错误消息。