暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

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

原创 由迪 2020-08-24
1007

要启用或禁用插件,请启用或禁用 rewriter_enabled系统变量。默认情况下,Rewriter插件在安装时处于启用状态(请参见 第5.6.4.1节“安装或卸载Rewriter Query Rewrite插件”)。要显式设置初始插件状态,可以在服务器启动时设置变量。例如,要在选项文件中启用插件,请使用以下几行:

[mysqld] rewriter_enabled=ON

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

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

假设Rewriter插件已启用,它将检查并可能修改服务器收到的每个可重写语句。该插件根据其在内存中的重写规则缓存来确定是否重写语句,这些缓存是从数据库中的rewrite_rules表加载的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();

小费

如果您的重写规则似乎无法正常工作,请通过调用来确保已重新加载规则表 flush_rewrite_rules()

当插件从规则表中读取每个规则时,它会从模式和摘要哈希值中计算出规范化(语句摘要)形式,并使用它们来更新 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 ?

有关语句摘要,规范化语句和摘要哈希值的信息,请参见 第26.10节“性能模式语句摘要和采样”

如果由于某些错误而无法加载规则,则调用 flush_rewrite_rules()会产生错误:

mysql> CALL query_rewrite.flush_rewrite_rules(); ERROR 1644 (45000): Loading of some rule(s) failed.

发生这种情况时,插件会将错误消息写入 message规则行的列以传达问题。检查 rewrite_rules表中具有非NULL message列值的行,以查看存在什么问题。

模式使用与准备好的语句相同的语法(请参见 第13.5.1节“ 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

语句不必重写为相同类型的语句。以下示例加载了一条DELETEUPDATE语句重写为 语句的规则 :

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类型 )。

更改意味着应用程序必须引用 user_id而不是idWHERE子句中。但是,如果有些旧应用程序无法编写以更改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使用第一条规则来匹配使用限定表名的语句。它使用秒数来匹配使用非限定名称的语句,但前提是默认数据库为appdb(中的值pattern_database)。

语句匹配如何工作

Rewriter插件使用语句摘要和摘要哈希值来分阶段将传入语句与重写规则进行匹配。的 max_digest_length系统变量决定用于计算语句摘要缓冲区的大小。较大的值使摘要的计算可以区分较长的语句。较小的值使用较少的内存,但增加了较长的语句与相同的摘要值冲突的可能性。

插件将每个语句与重写规则进行匹配,如下所示:

  1. 计算语句摘要哈希值,并将其与规则摘要哈希值进行比较。这会产生误报,但可以作为快速拒绝测试。
  2. 如果语句摘要哈希值与任何模式摘要哈希值匹配,则将语句的规范化(语句摘要)形式与匹配规则模式的规范化形式进行匹配。
  3. 如果规范化的语句与规则匹配,请比较该语句和模式中的文字值。一个 ?在模式字符匹配在声明中的任何文字值。如果该语句准备了一条语句,?则模式?中的语句也会匹配。否则,相应的文字必须相同。

如果有多个规则与一条语句匹配,则该插件使用哪条重写语句是不确定的。

如果某个模式包含的标记多于替换标记,则该插件会丢弃多余的数据值。如果模式包含的标记少于替换的标记,则为错误。加载规则表时,插件会注意到这一点,将错误消息写入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 | +-----------------------------------+-------+

有关这些变量的描述,请参见 第5.6.4.3.4节“重写器查询重写插件状态变量”

通过调用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值必须 与加载规则表时的全局值相同,否则规则匹配将不适用于该客户端。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论