首页 > 数据库 > > 5.5 Mysql 服务器插件
参考官方文档:
https://dev.mysql.com/doc/refman/5.7/en/server-plugins.html
MySQL支持一个可以创建服务器组件的插件API。 可以在服务器启动时加载插件,或者在运行时加载和卸载插件,而无需重新启动服务器。 此接口支持的组件包括但不限于存储引擎,INFORMATION_SCHEMA表,全文分析器插件,分区支持和服务器扩展。
MySQL发行版包含几个实现服务器扩展的插件:
以下各节介绍如何安装和卸载插件,以及如何在运行时确定安装哪些插件并获取有关插件的信息
5.5.1 安装和卸载插件
服务器插件必须先加载到服务器才能使用。 MySQL在服务器启动和运行时支持插件加载。 也可以在启动时控制加载插件的激活状态,并在运行时卸载它们。
加载插件时,有关它的信息在运行时可以从INFORMATION_SCHEMA.PLUGINS表和SHOW PLUGINS语句获得。
安装插件
在使用服务器插件之前,必须使用以下方法之一安装服务器插件。 在说明中,plugin_name代表插件名称,例如innodb,csv或validate_password。
Built-in plugins:
服务器会自动识别内置的插件。 通常,服务器在启动时启用该插件。 一些内置的插件允许用--plugin_name [= activation_state]选项来改变它。
在mysql.plugin系统表中注册的插件:
mysql.plugin表充当插件的注册表(除了内置插件,不需要注册)。 启动时,服务器加载表中列出的每个插件。 通常,对于从mysql.plugin表中加载的插件,服务器还会启用该插件。 这可以通过--plugin_name [= activation_state]选项进行更改。
如果服务器使用--skip-grant-tables选项启动,则不会查询mysql.plugin表,也不会加载列出的插件。
使用命令行选项命名的插件:
位于插件库文件中的插件可以在服务器启动时加载--plugin-load,--plugin-load-add或(从MySQL 5.7.11开始) - 初始插件加载选项。 通常,对于启动时加载的插件,服务器还会启用该插件。 这可以通过--plugin_name [= activation_state]选项进行更改。
在插件和存储引擎已经在服务器启动序列期间初始化之后,--plugin-load和--plugin-load-add选项加载插件。 --early-plugin-load选项用于加载在内置插件和存储引擎初始化之前必须可用的插件。
每个插件加载选项的值都是以name=plugin_library和plugin_library值的分号分隔的列表。 每个名称都是要加载的插件的名称,而plugin_library是包含插件代码的库文件的名称。 如果插件库的名称没有任何前面的插件名称,则服务器会加载库中的所有插件。 服务器在由plugin_dir系统变量命名的目录中查找插件库文件。
插件加载选项不会在mysql.plugin表中注册任何插件。 对于后续重新启动,仅当再次给出--plugin-load,--plugin-load-add或--early-plugin-load时,服务器才会再次加载插件。 也就是说,该选项会生成一次性插件安装操作,该操作会持续进行单个服务器调用。
--plugin-load,--plugin-load-add和--early-plugin-load使得即使在给出--skip-grant-tables的情况下也可以加载插件(这会导致服务器忽略mysql.plugin表)。 --plugin-load,--plugin-load-add和--early-plugin-load也允许在运行时无法加载的插件在启动时加载。
--plugin-load-add选项补充了--plugin-load选项:
例如:这些选项:
--plugin-load=x --plugin-load-add=y
等价于这些选项:
--plugin-load="x;y"
但是这些选项:
--plugin-load-add=y --plugin-load=x
等价于这些选项
--plugin-load=x
使用INSTALL PLUGIN语句安装的插件:
位于插件库文件中的插件可以在运行时用INSTALL PLUGIN语句加载。 该语句还将插件注册到mysql.plugin表中,以使服务器在后续重新启动时加载它。 因此,INSTALL PLUGIN需要mysql.plugin表的INSERT权限。
插件库文件的基本名称取决于您的平台。 对于Unix和类Unix系统,常见后缀是.so,对于Windows,则为.dll。
示例:--plugin-load选项在服务器启动时安装一个插件。 要从名为somepluglib.so的插件库文件安装名为myplugin的插件,请在my.cnf文件中使用以下行:
[mysqld]
plugin-load=myplugin=somepluglib.so
在这种情况下,该插件未在mysql.plugin中注册。 不使用--plugin-load选项重新启动服务器会导致插件在启动时不加载。
或者,INSTALL PLUGIN语句会导致服务器在运行时从库文件加载插件代码:
INSTALL PLUGIN myplugin SONAME 'somepluglib.so';
INSTALL PLUGIN也会导致“永久”插件注册:该插件列在mysql.plugin表中,以确保服务器在后续重新启动时加载它。
许多插件可以在服务器启动时或运行时加载。 但是,如果插件的设计必须在服务器启动期间加载并初始化,则尝试在运行时使用INSTALL PLUGIN加载它会产生错误:
mysql> INSTALL PLUGIN myplugin SONAME 'somepluglib.so'; ERROR 1721 (HY000): Plugin 'myplugin' is marked as not dynamically installable. You have to stop the server to install it.
在这种情况下,您必须使用--plugin-load,--plugin-load-add或--early-plugin-load。
如果插件在mysql.plugin表中使用--plugin-load,--plugin-load-add或--early-plugin-load选项和(作为更早的INSTALL PLUGIN语句的结果)命名, 服务器启动但将这些消息写入错误日志:
[ERROR] Function 'plugin_name' already exists [Warning] Couldn't load plugin named 'plugin_name' with soname 'plugin_object_file'.
控制插件活动状态
如果服务器在启动时知道插件(例如,因为插件使用--plugin-load选项进行了命名或在mysql.plugin表中注册),则服务器默认加载并启用该插件。 使用--plugin_name [= activation_state]启动选项可以控制此插件的激活状态,其中plugin_name是要影响的插件的名称,例如innodb,csv或validate_password。 与其他选项一样,破折号和下划线在选项名称中可以互换。 另外,激活状态值不区分大小写。 例如,--my_plugin = ON和--my-plugin = on是等效的。
通知服务器禁用该插件。 这可能不适用于某些内置插件,如mysql_native_password。
通知服务器启用该插件。 (将选项指定为不带值的--plugin_name具有相同的效果。)如果插件无法初始化,则服务器在禁用插件的情况下运行。
告诉服务器启用插件,但是如果插件初始化失败,服务器不会启动。 换句话说,这个选项强制服务器在插件启用下运行,或则不运行。
像FORCE一样,但是另外防止插件在运行时被卸载。 如果用户尝试使用UNINSTALL PLUGIN执行此操作,则会发生错误。
插件激活状态在INFORMATION_SCHEMA.PLUGINS表的LOAD_OPTION列中可见。
假设CSV,BLACKHOLE和ARCHIVE是内置的可插拔存储引擎,并且希望服务器在启动时加载它们,但要符合以下条件:如果CSV初始化失败,则必须允许服务器运行,要求BLACKHOLE必须初始化成功, 并应该禁用ARCHIVE。 要做到这一点,请在选项文件中使用这些行:
[mysqld]
csv=ON
blackhole=FORCE
archive=OFF
--enable-plugin_name选项格式是--plugin_name = ON的同义词。 --disable-plugin_name和--skip-plugin_name选项格式是--plugin_name = OFF的同义词。
如果某个插件被禁用,显式使用OFF或隐式使用,因为它已启用但未初始化,则需要该插件的服务器操作方面将发生变化。 例如,如果插件实现存储引擎,则存储引擎的现有表变得不可访问,并且尝试为存储引擎创建新表会生成使用默认存储引擎的表,除非启用了NO_ENGINE_SUBSTITUTION SQL模式在发生这种情况时引起一个错误。
禁用插件可能需要对其他选项进行调整。 例如,如果使用--skip-innodb启动服务器以禁用InnoDB,则启动时可能需要省略其他innodb_xxx选项。 另外,因为InnoDB是默认的存储引擎,所以,除非您使用--default_storage_engine指定另一个可用的存储引擎 ,其不会启动。 您还必须设置--default_tmp_storage_engine。
卸载插件
在运行时,UNINSTALL PLUGIN语句禁用并卸载服务器已知的插件。 该语句将卸载该插件,并将其从mysql.plugin表中删除,如果它在其中注册的话。 因此,UNINSTALL PLUGIN语句需要mysql.plugin表的DELETE权限。 如果插件不再注册在表中,服务器在后续重新启动不会自动加载插件。
UNINSTALL PLUGIN可以卸载一个插件,无论它是在运行时用INSTALL PLUGIN加载的还是在启动时用插件加载选项加载的,受到以下条件的限制:
要卸载当前在服务器启动时加载的插件,请使用此过程。
5.5.2 获取服务器插件信息
有几种方法可以确定服务器中安装了哪些插件:
mysql> SELECT * FROM information_schema.PLUGINS\G
mysql> SHOW PLUGINS\G
5.5.4 查询重写插件
从MySQL 5.7.6开始,MySQL服务器支持查询重写插件,可以在服务器执行它们之前检查并可能修改服务器接收的语句。
MySQL发行版包括一个名为Rewriter的Postparse查询重写插件和用于安装插件及其相关组件的脚本。 这些组件共同提供SELECT重写功能:
以下各节介绍如何安装和使用重写器插件,并为其关联组件提供参考信息。
5.5.4.1 安装和卸载 重写查询插件
注意:
如果安装了,重写器插件即使在禁用时也会涉及一些开销。 为了避免这种开销,除非你打算使用它,否则不要安装插件。
要安装或卸载重写器查询重写插件,请选择位于MySQL安装的共享目录中的approropriate脚本:
按如下所示运行所选脚本:
shell> mysql -u root -p < install_rewriter.sql
Enter password: (enter root password here)
这里的示例使用install_rewriter.sql安装脚本。 如果您选择不同的脚本,请进行适当的替换。
运行安装脚本会安装并启用插件。 要验证,请连接到服务器并执行以下语句:
SHOW GLOBAL VARIABLES LIKE 'rewriter_enabled';
5.5.4.2 使用查询重写插件
要启用或禁用插件,请启用或禁用rewriter_enabled系统变量。 默认情况下,重写器插件在安装时启用。 要明确设置初始插件状态,可以在服务器启动时设置变量。 例如,要在选项文件中启用插件,请使用以下行:
[mysqld]
rewriter_enabled=ON
也可以在运行时启用或禁用插件:
mysql> SET GLOBAL rewriter_enabled = ON;
mysql> SET GLOBAL rewriter_enabled = OFF;
假设重写器插件已启用,它将检查并可能修改服务器接收的每个SELECT语句。 该插件根据重写规则的内存缓存确定是否重写语句,该规则是从query_rewrite数据库的rewrite_rules表中加载的。
增加重写规则
要为 Rewriter 插件添加规则,请将行添加到rewrite_rules表中,然后调用flush_rewrite_rules()存储过程以将表中的规则加载到插件中。 以下示例创建一个简单的规则来匹配选择单个文字值的语句:
mysql> INSERT INTO query_rewrite.rewrite_rules (pattern, replacement) VALUES('SELECT ?', 'SELECT ? + 1');
生成的表内容如下所示:
mysql> SELECT * FROM query_rewrite.rewrite_rules\G
该规则指定一个模式模板,指示要匹配哪些SELECT语句以及指示如何重写匹配语句的替换模板。 但是,将规则添加到rewrite_rules表中并不足以导致重写器插件使用该规则。 您必须调用flush_rewrite_rules()将表内容加载到插件内存中缓存中:
mysql> CALL query_rewrite.flush_rewrite_rules()
tip:
如果您的重写规则似乎不能正常工作,请确保您已通过调用flush_rewrite_rules()重新加载规则表。
当插件从规则表中读取每条规则时,它会根据模式和 digest 散列值计算标准化(语句 digest )形式,并使用它们更新normalized_pattern和pattern_digest列:
mysql> SELECT * FROM query_rewrite.rewrite_rules\G
模式使用与预处理语句相同的语法。 在模式模板中,? 字符充当与数据值匹配的参数标记。 参数标记只能用于出现数据值的地方,而不能用于SQL关键字,标识符等等。 这个? 字符不应包含在引号内。
像模式一样,replacement 可以包含?字符。 对于匹配模式模板的语句,插件会重写它,replacement ? 参数标记在替换中使用与模式中相应标记匹配的数据值。 结果是一个完整的语句字符串。 插件会要求服务器解析它,并将结果作为重写语句的表示返回给服务器。
添加并加载规则后,根据语句是否符合规则模式,检查是否发生重写:
mysql> SELECT PI();
mysql> SELECT 10;
+--------+ | 10 + 1 |
+--------+ | 11 | +--------+
1 row in set, 1 warning (0.00 sec)
第一个SELECT语句没有重写,但是第二个是。 第二条语句说明了当插件重写一条语句时,它会产生一条警告消息。 要查看消息,请使用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
要启用或禁用现有规则,请修改其 enabled 列并将表重新加载到插件中。 要禁用规则1:
mysql> UPDATE query_rewrite.rewrite_rules SET enabled = 'NO' WHERE id = 1;
mysql> CALL query_rewrite.flush_rewrite_rules();
这使您可以在不从表格中删除规则的情况下取消规则活动。
重新启用规则1:
mysql> UPDATE query_rewrite.rewrite_rules SET enabled = 'YES' WHERE id = 1;
mysql> CALL query_rewrite.flush_rewrite_rules();
rewrite_rules表包含一个pattern_database列,Rewriter使用该列来匹配不符合数据库名称限定的表名:
--表名是否使用前缀限定数据库的情况
假设一个名为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。 但是,如果有旧的应用程序无法写入来更改它们生成的SELECT查询,则它们将不再正常工作。 重写器插件可以解决这个问题。 要匹配和重写语句,无论它们是否符合表名,请添加以下两条规则并重新加载规则表:
mysql> INSERT INTO query_rewrite.rewrite_rules -> (pattern, replacement) VALUES( -> 'SELECT * FROM appdb.users WHERE id = ?', -> 'SELECT * FROM appdb.users WHERE user_id = ?' -> );
mysql> INSERT INTO query_rewrite.rewrite_rules -> (pattern, replacement, pattern_database) VALUES( -> 'SELECT * FROM users WHERE id = ?', -> 'SELECT * FROM users WHERE user_id = ?', -> 'appdb' -> );
mysql> CALL query_rewrite.flush_rewrite_rules();
--也就数说,字段变更,代码不用变更,但其实往往变更都是由应用发起,在这里略显鸡肋。
重写器使用第一条规则来匹配使用限定表名的语句。 它使用第二个匹配使用非限定名称的语句,但仅当默认数据库是appdb(pattern_database中的值)时才使用该语句。
语句匹配如何工作
重写器插件使用语句摘要和摘要散列值来分阶段地将传入语句与重写规则进行匹配。 max_digest_length系统变量确定用于计算语句摘要的缓冲区的大小。 较大的值可用于计算区分较长语句的摘要。 较小的值使用较少的内存,但增加了较长的语句与相同摘要值碰撞的可能性。
该插件将每条语句与重写规则进行匹配,如下所示:
如果多条规则匹配一条语句,则不确定插件使用哪一条语句来重写语句。
如果一个 pattern 包含比 replacement 更多的标记,则该插件会丢弃多余的数据值。 如果一个 pattern 包含的标记数量少于替换的标记数量,则这是一个错误 。当规则表被加载时,插件会注意到这一点,将错误消息写入规则行的 message 列来传达问题,并将Rewriter_reload_error状态变量设置为ON。
重写被准备的语句
准备好的语句在解析时(即准备时)被重写,而不是稍后执行。
准备好的语句与非准备语句的区别在于它们可能包含? 字符作为参数标记。 匹配一个? 在准备好的声明中,重写器模式必须包含? 在相同的位置。 假设重写规则有这种模式:
SELECT ?, 3
下表显示了几个准备好的SELECT语句以及规则模式是否匹配它们。
Prepared StatementWhether Pattern Matches StatementPREPARE s AS 'SELECT 3, 3'YesPREPARE s AS 'SELECT ?, 3'YesPREPARE s AS 'SELECT 3, ?'NoPREPARE s AS 'SELECT ?, ?'No
重写器插件操作信息
重写器插件通过几个状态变量提供有关其操作的信息:
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 |
当通过调用flush_rewrite_rules()存储过程加载规则表时,如果某些规则发生错误,则CALL语句会产生错误,并且插件将Rewriter_reload_error状态变量设置为ON:
重写器插件使用的字符集
当rewrite_rules表被加载到重写器插件中时,插件使用character_set_client系统变量的当前全局值来解释语句。 如果全局character_set_client值随后发生更改,则必须重新加载规则表。
客户端必须具有与加载规则表时,与全局值相同的会话character_set_client值,否则规则匹配将不适用于该客户端。
5.5.4.3 查询重写插件参考
以下讨论可作为与重写器查询重写插件相关组件的参考:
5.5.4.3.1 查询重写插件表
query_rewrite数据库中的rewrite_rules表为重写器插件用于决定是否重写语句的规则提供持久存储。
用户通过修改该表中存储的规则集与插件进行通信。 该插件通过设置表的message列与用户进行通信。
注意:
规则表由flush_rewrite_rules存储过程加载到插件中。 除非在最近的表修改之后调用该过程,否则表内容不一定对应于插件正在使用的一组规则。
rewrite_rules表具有以下列:
规则ID。 该列是表主键。 您可以使用该ID来唯一标识任何规则。
指示规则匹配的语句模式的模板。 使用 ? 以表示与数据值匹配的参数标记。
用于匹配语句中不合格表名的数据库。 如果相应的数据库和表名称相同,则语句中的合格表名与模式中的限定名匹配。 只有当默认数据库与pattern_database相同且表名相同时,语句中的非限定表名才与模式中的非限定名匹配。
指示如何重写与模式列值匹配的语句的模板。 使用 ? 以表示与数据值匹配的参数标记。 在重写的语句中,插件替换? 使用与pattern 中的相应标记匹配的数据值进行 replacement 的参数标记。
规则是否启用。 加载操作(通过调用flush_rewrite_rules()存储过程执行)只有当此列为YES(Y是MySQL 5.7.8之前的版本)时,才会将表中的规则加载到Rewriter内存中缓存中。
此列可以在不删除规则的情况下取消激活规则:将列设置为YES以外的值,并将表重新加载到插件中。
该插件使用此列与用户进行通信。 如果规则表加载到内存中时没有发生错误,插件会将消息列设置为NULL。 非NULL值表示错误,列内容是错误消息。 在这些情况下可能会发生错误:
如果发生加载错误,插件还会将Rewriter_reload_error状态变量设置为ON。
该列用于调试和诊断。 如果规则表加载到内存中时该列存在,插件会使用模式摘要更新它。 如果您试图确定为什么某些语句无法重写,此列可能很有用。
该列用于调试和诊断。 如果规则表加载到内存中时该列存在,则插件会使用规范化的模式更新它。 如果您试图确定为什么某些语句无法重写,此列可能很有用。
注意:
在MySQL 5.7.8之前,pattern_digest和normalized_pattern列是可选的:如果您使用install_rewriter_with_optional_columns.sql安装Rewriter插件,则会创建它们,但如果使用install_rewriter.sql则不会。
5.5.4.3.2查询重写插件程序和函数
重写器插件操作使用将规则表加载到其内存中缓存的存储过程以及帮助器用户定义的函数(UDF)。 在正常操作下,用户只调用存储过程。 UDF旨在由存储过程调用,而不是由用户直接调用。
此存储过程使用load_rewrite_rules()UDF将rewrite_rules表的内容加载到重写器内存中缓存中。 加载表后,它也会清除查询缓存。
调用flush_rewrite_rules()意味着COMMIT。
修改规则表以使插件从新表内容更新其缓存后,调用此过程。 如果发生任何错误,插件将为表中适当的规则行设置消息列,并将Rewriter_reload_error状态变量设置为ON。
这个UDF是flush_rewrite_rules()存储过程使用的帮助程序。
5.5.4.3.3查询重写插件系统变量
查询重写插件支持以下系统变量。 这些变量只有在插件已安装的情况下才可用
全局变量,布尔型,动态,默认是ON
全局变量,integer,动态,内部使用的参数
5.5.4.3.4 查询重写插件状态变量
已成功从rewrite_rules表加载到内存中供Rewriter插件使用的规则数目。
rewrite_rules表被加载到Rewriter插件使用的内存缓存中的次数。
查询重写插件自加载以来重写的查询数量。
最近一次rewrite_rules表加载到Rewriter插件使用的内存缓存中时是否发生错误。 如果该值为OFF,则不会发生错误。 如果该值为ON,则发生错误; 检查rewriter_rules表的消息列中是否有错误消息。
5.5.5 版本 Tokens
MySQL 5.7.8或更高版本的分发版本包括版本令牌(Version Tokens),该功能可以创建令牌并对其进行同步,这样应用程序可以用来防止访问不正确或过时数据的服务器。
版本令牌接口具有以下特征:
5.5.5.1 版本 Tokens 组件
版本令牌基于实现这些组件的插件库:
5.5.5.2 安装和卸载version tokens
注意:
如果安装,版本令牌会涉及一些开销。 为了避免这种开销,除非你打算使用它,否则不要安装它。
本节介绍如何安装或卸载版本令牌,这个在包含插件和用户定义函数的插件库文件中实现。
为了供服务器使用,插件库文件必须位于MySQL插件目录(由plugin_dir系统变量命名的目录)中。 如有必要,请在服务器启动时设置plugin_dir的值,以告知服务器插件目录的位置。
插件库文件基本名称是version_tokens。 每个平台的文件名后缀都不相同(例如,对于Unix和类Unix系是.so,Windows的是.dll)。
要安装版本令牌插件和UDF,请使用INSTALL PLUGIN和CREATE FUNCTION语句(根据需要为您的平台调整.so后缀):
INSTALL PLUGIN version_tokens SONAME 'version_token.so';
CREATE FUNCTION version_tokens_set RETURNS STRING SONAME 'version_token.so';
CREATE FUNCTION version_tokens_show RETURNS STRING SONAME 'version_token.so';
CREATE FUNCTION version_tokens_edit RETURNS STRING SONAME 'version_token.so';
CREATE FUNCTION version_tokens_delete RETURNS STRING SONAME 'version_token.so';
CREATE FUNCTION version_tokens_lock_shared RETURNS INT SONAME 'version_token.so';
CREATE FUNCTION version_tokens_lock_exclusive RETURNS INT SONAME 'version_token.so';
CREATE FUNCTION version_tokens_unlock RETURNS INT SONAME 'version_token.so';
您必须安装UDF来管理服务器的版本令牌列表,但是您还必须安装该插件,因为没有它,UDF将无法正常工作。
如果插件和UDF在主复制服务器上使用,请将它们安装在所有从属服务器上以避免复制问题。
按照刚刚描述的方式安装后,版本令牌插件和UDF将保持安装状态直到卸载。 要删除它们,请使用UNINSTALL PLUGIN和DROP FUNCTION语句:
UNINSTALL PLUGIN version_tokens;
DROP FUNCTION version_tokens_set;
DROP FUNCTION version_tokens_show;
DROP FUNCTION version_tokens_edit;
DROP FUNCTION version_tokens_delete;
DROP FUNCTION version_tokens_lock_shared;
DROP FUNCTION version_tokens_lock_exclusive;
DROP FUNCTION version_tokens_unlock;
5.5.5.3 使用 version Tokens
Version Tokens可用的场景是访问MySQL服务器集合的系统,但需要通过监视它们并根据负载变化调整服务器分配来进行管理以实现负载平衡。 这样的系统包括这些组件:
版本令牌允许服务器访问根据分配进行管理,而无需客户端重复查询服务器的分配情况:
如果某些时候管理应用程序需要更改服务器分配(例如,将其从允许写入更改为只读),则它会更改服务器的版本令牌列表并更新其缓存。
---类似于一个中间件管理
另一方面,管理应用程序可能已更改了服务器分配及其版本令牌列表。 在这种情况下,新的服务器分配现在可能与客户端要求不兼容。 服务器和客户端令牌列表之间发生令牌不匹配,并且服务器返回错误来回复该语句。 这表示客户端从管理应用程序缓存中刷新版本令牌信息并选择要与之通信的新服务器。
用于检测版本令牌错误和选择新服务器的客户端逻辑可以以不同的方式实现:
以下示例以更具体的形式说明了前面的讨论。
当版本令牌在给定服务器上初始化时,服务器的版本令牌列表为空。 令牌列表维护通过调用用户定义函数(UDF)来执行。 需要SUPER权限才能调用任何版本令牌UDF,因此令牌列表修改预计将由具有该权限的 management 或管理应用程序完成。
假设管理应用程序与客户端查询的一组服务器进行通信,以访问员工和产品数据库(分别命名为emp和prod)。 所有服务器都可以处理数据检索语句,但只有其中一些服务器可以进行数据库更新。 为了在数据库特定的基础上处理这个问题,管理应用程序在每台服务器上建立一个版本令牌列表。 在给定服务器的令牌列表中,令牌名称表示数据库名称,令牌值是read或 write 的,取决于数据库是以只读方式使用还是可读写的。
客户端应用程序通过设置系统变量来注册它们要求服务器匹配的版本令牌列表。 变量设置发生在特定客户端的基础上,因此不同的客户端可以注册不同的需求。 默认情况下,客户端令牌列表为空,与任何服务器令牌列表匹配。 当客户端将其令牌列表设置为非空值时,匹配可能成功或失败,具体取决于服务器版本令牌列表。
要为服务器定义 version token列表,管理应用程序将调用version_token_set()UDF。 (还有用于修改和显示令牌列表的UDF,稍后介绍)。例如,应用程序可能会将这些语句发送一个3个服务器的组:
Server 1:
mysql> SELECT version_tokens_set('emp=read;prod=read'); +------------------------------------------+ | version_tokens_set('emp=read;prod=read') | +------------------------------------------+ | 2 version tokens set. | +------------------------------------------+
Server 2:
mysql> SELECT version_tokens_set('emp=write;prod=read'); +-------------------------------------------+ | version_tokens_set('emp=write;prod=read') | +-------------------------------------------+ | 2 version tokens set. | +-------------------------------------------+
Server 3:
mysql> SELECT version_tokens_set('emp=read;prod=write'); +-------------------------------------------+ | version_tokens_set('emp=read;prod=write') | +-------------------------------------------+ | 2 version tokens set. | +-------------------------------------------+
每种情况下的token列表都被指定为分号分隔的名称=值对列表。 得到的令牌列表值导致这些服务器分配:
除了为每个服务器分配版本令牌列表之外,管理应用程序还维护一个反映服务器分配的缓存。
在与服务器通信之前,客户端应用程序会联系管理应用程序并检索有关服务器分配的信息。 然后客户端根据这些分配选择一台服务器。 假设客户端想要在emp数据库上执行读写操作。 根据前面的分配,只有服务器2符合要求。 客户端连接到服务器2并通过设置其version_tokens_session系统变量在那里注册其服务器要求:
mysql> SET @@session.version_tokens_session = 'emp=write';
对于客户端向服务器2发送的后续语句,服务器会将其自己的版本令牌列表与客户端列表进行比较,以检查它们是否匹配。 如果是这样,语句正常执行:
mysql> UPDATE emp.employee SET salary = salary * 1.1 WHERE id = 4981;
Query OK, 1 row affected (0.07 sec) Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT last_name, first_name FROM emp.employee WHERE id = 4981;
服务器和客户端版本令牌列表之间的差异可以通过两种方式发生:
只要服务器2的分配不变,客户端就会继续使用它进行读取和写入。 但假设管理应用程序想要更改服务器分配,以便emp数据库的写入必须发送到服务器1而不是服务器2.为此,它使用version_tokens_edit()修改两台服务器上的emp令牌值(以及 更新其服务器分配的缓存):
Server 1:
mysql> SELECT version_tokens_edit('emp=write'); +----------------------------------+ | version_tokens_edit('emp=write') | +----------------------------------+ | 1 version tokens updated. | +----------------------------------+
Server 2:
mysql> SELECT version_tokens_edit('emp=read'); +---------------------------------+ | version_tokens_edit('emp=read') | +---------------------------------+ | 1 version tokens updated. | +---------------------------------+
version_tokens_edit()修改服务器令牌列表中的命名令牌,并保持其他令牌不变。
客户端下次向服务器2发送语句时,其自己的令牌列表不再与服务器令牌列表匹配,并且发生错误:
mysql> UPDATE emp.employee SET salary = salary * 1.1 WHERE id = 4982; ERROR 3136 (42000): Version token mismatch for emp. Correct value read
在这种情况下,客户端应联系管理应用程序以获取有关服务器分配的更新信息,选择新服务器并将失败的语句发送到新服务器。
注意:
每个客户端必须通过根据向特定服务器注册的令牌列表仅发送语句来配合版本令牌。 例如,如果客户端注册了'emp = read'的令牌列表,则Version Tokens中不会阻止客户端为emp数据库发送更新。 客户端本身必须避免这样做。
--不会禁止写语句,也就是说客户端本身业务还是要划分清楚。
对于从客户端接收到的每个语句,服务器隐式使用锁定,如下所示:
服务器使用共享锁,以便可以在不阻塞的情况下进行多个会话的比较,同时防止任何尝试获取排他锁的会话的令牌更改,它会处理服务器令牌列表中相同名称的令牌。
前面的示例仅使用版本令牌插件库中包含的几个用户定义,但还有其他一些。 一组UDF允许对服务器的版本令牌列表进行操作和检查。 另一组UDF允许版本令牌被锁定和解锁。
这些UDF允许创建,更改,删除和检查服务器的版本令牌列表:
如果成功,每个函数都会返回一个二进制字符串,指示发生了什么操作。 以下示例建立服务器令牌列表,通过添加新令牌来修改它,删除一些令牌并显示结果令牌列表:
mysql> SELECT version_tokens_set('tok1=a;tok2=b'); +-------------------------------------+ | version_tokens_set('tok1=a;tok2=b') | +-------------------------------------+
| 2 version tokens set. |
+-------------------------------------+
mysql> SELECT version_tokens_edit('tok3=c'); +-------------------------------+ | version_tokens_edit('tok3=c') | +-------------------------------+ |
1 version tokens updated.
mysql> SELECT version_tokens_delete('tok2;tok1'); +------------------------------------+ | version_tokens_delete('tok2;tok1') | +------------------------------------+ | 2 version tokens deleted. | +------------------------------------+
mysql> SELECT version_tokens_show();
+-----------------------+ | version_tokens_show() | +-----------------------+ | tok3=c; | +-----------------------+
如果令牌列表格式错误,则会发生警告:
mysql> SELECT version_tokens_set('tok1=a; =c'); +----------------------------------+ | version_tokens_set('tok1=a; =c') | +----------------------------------+ |
1 version tokens set.
| +----------------------------------+ 1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G *************************** 1. row ***************************
Level: Warning Code: 42000 Message: Invalid version token pair encountered. The list provided is only partially updated. 1 row in set (0.00 sec)
如前所述,版本令牌是使用分号分隔的 name=value 对列表定义的。 考虑调用version_tokens_set():
mysql> SELECT version_tokens_set('tok1=b;;; tok2= a = b ; tok1 = 1\'2 3"4') +---------------------------------------------------------------+ | version_tokens_set('tok1=b;;; tok2= a = b ; tok1 = 1\'2 3"4') | +---------------------------------------------------------------+ | 3 version tokens set. | +---------------------------------------------------------------+
版本令牌解释参数如下:
根据这些规则,前面的version_tokens_set()调用会生成带有两个令牌的令牌列表:tok1的值为1'2 3"4,tok2的值为a = b。要验证此操作,请调用version_tokens_show():
mysql> SELECT version_tokens_show(); +--------------------------+
| version_tokens_show() |
+--------------------------+
| tok2=a = b;tok1=1'2 3"4; |
+--------------------------+
如果令牌列表包含两个令牌,为什么version_tokens_set()返回3个令牌的值? 这是因为原始记号列表包含tok1的两个定义,而第二个定义替换了第一个。
版本令牌令牌操作UDF将这些约束放置在令牌名称和值上:
版本令牌还包括一组使用令牌锁定和解锁的UDF:
每个锁定函数返回非零值以获得成功。 否则,会发生错误:
mysql> SELECT version_tokens_lock_shared('lock1', 'lock2', 0); +-------------------------------------------------+ | version_tokens_lock_shared('lock1', 'lock2', 0) | +-------------------------------------------------+
| 1 |
+-------------------------------------------------+
mysql> SELECT version_tokens_lock_shared(NULL, 0); ERROR 3131 (42000): Incorrect locking service lock name '(null)'.
使用版本令牌锁定功能进行锁定是建议性的; 应用程序必须同意合作。
可以锁定不存在的令牌名称。 这不会创建令牌。
注意:
版本令牌锁定功能基于第28.3.1节“锁定服务”中描述的锁定服务,因此对于共享锁和排他锁具有相同的语义。 (版本令牌使用内置于服务器中的锁定服务例程,而不是锁定服务UDF接口,因此无需安装这些UDF即可使用版本令牌。)版本令牌获取的锁使用version_token_locks的锁定服务名称空间。 锁定服务锁可以使用性能模式进行监控,因此版本令牌锁也是如此。
对于版本令牌锁定功能,令牌名称参数完全按照指定使用。 周围的空白不被忽略,=和; 字符是允许的。 这是因为版本令牌只是将令牌名称传递给锁定服务。
5.5.5.4 Version Tokes 参考
version tokens 函数
版本令牌插件库包含多个用户定义的函数。 一组UDF允许对服务器的版本令牌列表进行操作和检查。 另一组UDF允许版本令牌被锁定和解锁。 需要SUPER权限才能调用任何版本令牌UDF。
以下UDF允许创建,更改,删除和检查服务器的版本令牌列表。 解释name_list和token_list参数(包括空格处理)的发生,如第5.5.5.3节“使用版本令牌”中所述,其中提供了有关用于指定令牌的语法的详细信息以及其他示例。
使用name_list参数从服务器的版本令牌列表中删除令牌,并返回指示操作结果的二进制字符串。 name_list是要删除的版本令牌名称的分号分隔列表。
mysql> SELECT version_tokens_delete('tok1;tok3'); +------------------------------------+ | version_tokens_delete('tok1;tok3') | +------------------------------------+ | 2 version tokens deleted. | +------------------------------------+
从MySQL 5.7.9开始,NULL参数被视为一个空字符串,它对令牌列表没有影响。
version_tokens_delete()删除其参数中命名的令牌(如果它们存在)。 (删除不存在的令牌不是错误)要清除令牌列表而不知道列表中的令牌是否包含,请将NULL或不包含令牌的字符串传递给version_tokens_set():
mysql> SELECT version_tokens_set(NULL); +------------------------------+
| version_tokens_set(NULL) |
+------------------------------+ | Version tokens list cleared. | +------------------------------+
mysql> SELECT version_tokens_set(''); +------------------------------+
| version_tokens_set('') |
+------------------------------+ | Version tokens list cleared. | +------------------------------+
使用token_list参数修改服务器的版本令牌列表,并返回指示操作结果的二进制字符串。 token_list是以分号分隔的 name=value 对列表,用于指定要定义的每个标记的名称及其值。 如果存在令牌,则使用给定值更新其值。 如果令牌不存在,则使用给定的值创建该令牌。 如果参数为NULL或不包含标记的字符串,则token列表保持不变。
mysql> SELECT version_tokens_set('tok1=value1;tok2=value2'); +-----------------------------------------------+ | version_tokens_set('tok1=value1;tok2=value2') | +-----------------------------------------------+ | 2 version tokens set. | +-----------------------------------------------+
mysql> SELECT version_tokens_edit('tok2=new_value2;tok3=new_value3'); +--------------------------------------------------------+ | version_tokens_edit('tok2=new_value2;tok3=new_value3') | +--------------------------------------------------------+ | 2 version tokens updated. | +--------------------------------------------------------+
使用token_list参数中定义的标记替换服务器的版本标记列表,并返回一个表示操作结果的二进制字符串。 token_list是以分号分隔的 name=value 对列表,用于指定要定义的每个标记的名称及其值。 如果参数为NULL或不包含令牌的字符串,则清除令牌列表。
mysql> SELECT version_tokens_set('tok1=value1;tok2=value2'); +-----------------------------------------------+ | version_tokens_set('tok1=value1;tok2=value2') | +-----------------------------------------------+ | 2 version tokens set. | +-----------------------------------------------+
将服务器的版本令牌列表作为包含以分号分隔的 name=value对列表的二进制字符串形式返回。
mysql> SELECT version_tokens_show(); +--------------------------+
| version_tokens_show() |
+--------------------------+
| tok2=value2;tok1=value1; |
+--------------------------+
以下UDF允许版本令牌被锁定和解锁:
获取一个或多个版本令牌的独占锁定,由名称指定为字符串,如果在给定的超时值内没有获取到锁定,则超时并返回错误。
mysql> SELECT version_tokens_lock_exclusive('lock1', 'lock2', 10);
+-----------------------------------------------------+ | version_tokens_lock_exclusive('lock1', 'lock2', 10) | +-----------------------------------------------------+
| 1 |
+-----------------------------------------------------+
该函数在5.7.8中添加了名为vtoken_get_write_locks()的MySQL 5.7.8并重命名为version_tokens_lock_exclusive()。
获取一个或多个版本令牌的共享锁,由名称指定为字符串,如果在给定的超时值内没有获取到锁,则超时并返回错误。
mysql> SELECT version_tokens_lock_shared('lock1', 'lock2', 10);
+--------------------------------------------------+ | version_tokens_lock_shared('lock1', 'lock2', 10) | +--------------------------------------------------+
| 1 |
+--------------------------------------------------+
该函数在5.7.8中添加了名为vtoken_get_read_locks()的MySQL 5.7.8并重命名为version_tokens_lock_shared()。
使用version_tokens_lock_exclusive()和version_tokens_lock_shared()释放在当前会话中获取的所有锁定。
mysql> SELECT version_tokens_unlock(); +-------------------------+
| version_tokens_unlock() |
+-------------------------+
| 1 |
+-------------------------+
该函数在5.7.9中添加了名为vtoken_release_locks()的MySQL 5.7.8并重命名为version_tokens_unlock()。
锁定函数具有以下特点:
version Tokens system 变量
版本令牌支持以下系统变量。 除非安装了版本令牌插件,否则这些变量不可用
系统变量:
全局+会话,动态,字符串类型,默认值是null
此变量的会话值指定客户端版本令牌列表,并指示客户端会话要求服务器版本令牌列表具有的令牌。
如果version_tokens_session变量为NULL(默认值)或具有空值,则任何服务器版本令牌列表都匹配。 (实际上,一个空值会禁用匹配需求。)
如果version_tokens_session变量具有非空值,则其值与服务器版本令牌列表之间的任何不匹配都会导致会话发送到服务器的任何语句出错。 在这些条件下发生不匹配:
服务器版本令牌列表不包含未在version_tokens_session值中指定的令牌。
假设管理应用程序已经设置了服务器令牌列表,如下所示:
mysql> SELECT version_tokens_set('tok1=a;tok2=b;tok3=c'); +--------------------------------------------+ | version_tokens_set('tok1=a;tok2=b;tok3=c') | +--------------------------------------------+ | 3 version tokens set. | +--------------------------------------------+
客户端通过设置version_tokens_session值来注册它需要服务器匹配的令牌。 然后,对于客户端发送的每个后续语句,服务器都会根据客户端version_tokens_session值检查其标记列表,并在出现不匹配时产生错误:
mysql> SET @@session.version_tokens_session = 'tok1=a;tok2=b'; mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+
mysql> SET @@session.version_tokens_session = 'tok1=b';
mysql> SELECT 1; ERROR 3136 (42000): Version token mismatch for tok1. Correct value a
第一个SELECT成功,因为客户端令牌tok1和tok2出现在服务器令牌列表中,并且每个令牌在服务器列表中具有相同的值。 第二个SELECT失败,因为虽然tok1存在于服务器令牌列表中,但它具有与客户端指定的值不同的值。
此时,除非服务器令牌列表发生更改以使其再次匹配,否则客户端发送的任何语句都会失败。 假设管理应用程序如下更改服务器令牌列表:
mysql> SELECT version_tokens_edit('tok1=b'); +-------------------------------+ | version_tokens_edit('tok1=b') | +-------------------------------+ | 1 version tokens updated. | +-------------------------------+
mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| tok3=c;tok1=b;tok2=b; |
+-----------------------+
现在,客户端version_tokens_session值与服务器令牌列表匹配,并且客户端可以再次成功执行语句:
mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+
这个变量在 MYSQL 5.7.8被增加
全局+会话,非动态,整形,默认值为0
这个变量由内部使用,在5.7.8被添加
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69946831/viewspace-3168270/ ,如需转载,请注明出处,否则将追究法律责任。