Documentation Home

5.6.6.3 使用版本令牌

在使用版本令牌之前,请按照 第 5.6.6.2 节“安装或卸载版本令牌”中提供的说明进行安装。

Version Tokens 可以发挥作用的一个场景是访问一组 MySQL 服务器但需要通过监视它们并根据负载变化调整服务器分配来管理它们以达到负载平衡目的的系统。这样的系统包括以下元素:

  • 要管理的 MySQL 服务器的集合。

  • 与服务器通信并将它们组织成高可用性组的管理或管理应用程序。组服务于不同的目的,每个组中的服务器可能有不同的分配。某个组内服务器的分配可以随时更改。

  • 访问服务器以检索和更新数据的客户端应用程序,根据分配给它们的目的选择服务器。例如,客户端不应向只读服务器发送更新。

版本令牌允许根据分配管理服务器访问,而无需客户端重复查询服务器有关其分配的信息:

  • 管理应用程序执行服务器分配并在每个服务器上建立版本令牌以反映其分配。应用程序缓存此信息以提供对其的中央访问点。

    如果在某个时候管理应用程序需要更改服务器分配(例如,将其从允许写入更改为只读),它会更改服务器的版本令牌列表并更新其缓存。

  • 为了提高性能,客户端应用程序从管理应用程序获取缓存信息,使它们能够避免为每个语句检索有关服务器分配的信息。根据它发出的语句类型(例如,读取与写入),客户端选择合适的服务器并连接到它。

  • 此外,客户端向服务器发送它自己的特定于客户端的版本令牌,以注册它需要的服务器分配。对于客户端发送给服务器的每个语句,服务器都会将自己的令牌列表与客户端令牌列表进行比较。如果服务器令牌列表包含客户端令牌列表中存在的所有具有相同值的令牌,则存在匹配并且服务器执行该语句。

    另一方面,管理应用程序可能更改了服务器分配及其版本令牌列表。在这种情况下,新的服务器分配现在可能与客户端要求不兼容。服务器和客户端令牌列表之间发生令牌不匹配,并且服务器在回复语句时返回错误。这指示客户端从管理应用程序缓存中刷新其版本令牌信息,并选择一个新的服务器进行通信。

可以通过不同的方式实现用于检测版本令牌错误和选择新服务器的客户端逻辑:

  • 客户端可以自行处理所有版本令牌注册、不匹配检测和连接切换。

  • 这些操作的逻辑可以在管理客户端和 MySQL 服务器之间连接的连接器中实现。这样的连接器可能会处理不匹配错误检测并自行重新发送语句,或者它可能会将错误传递给应用程序并让应用程序重新发送语句。

以下示例以更具体的形式说明了前面的讨论。

当版本令牌在给定服务器上初始化时,服务器的版本令牌列表为空。令牌列表维护是通过调用函数来执行的。调用任何 Version Token 函数都需要 VERSION_TOKEN_ADMIN特权(或已弃用的特权 ),因此令牌列表修改应由具有该特权的管理或管理应用程序完成。SUPER

假设管理应用程序与一组服务器通信,客户端查询这些服务器以访问员工和产品数据库(分别命名为empprod)。所有服务器都被允许处理数据检索语句,但只有其中一些服务器被允许进行数据库更新。为了在特定于数据库的基础上处理这个问题,管理应用程序在每个服务器上建立一个版本令牌列表。在给定服务器的令牌列表中,令牌名称代表数据库名称,令牌值是readwrite 取决于数据库是否必须以只读方式使用或是否可以读取和写入。

客户端应用程序通过设置系统变量注册它们需要服务器匹配的版本令牌列表。变量设置发生在特定于客户的基础上,因此不同的客户可以注册不同的要求。默认情况下,客户端令牌列表为空,与任何服务器令牌列表匹配。当客户端将其令牌列表设置为非空值时,匹配可能成功或失败,具体取决于服务器版本令牌列表。

要为服务器定义版本令牌列表,管理应用程序调用该 version_tokens_set()函数。(还有用于修改和显示令牌列表的函数,稍后描述。)例如,应用程序可能将这些语句发送到一组三个服务器:

服务器 1:

mysql> SELECT version_tokens_set('emp=read;prod=read');
+------------------------------------------+
| version_tokens_set('emp=read;prod=read') |
+------------------------------------------+
| 2 version tokens set.                    |
+------------------------------------------+

服务器 2:

mysql> SELECT version_tokens_set('emp=write;prod=read');
+-------------------------------------------+
| version_tokens_set('emp=write;prod=read') |
+-------------------------------------------+
| 2 version tokens set.                     |
+-------------------------------------------+

服务器 3:

mysql> SELECT version_tokens_set('emp=read;prod=write');
+-------------------------------------------+
| version_tokens_set('emp=read;prod=write') |
+-------------------------------------------+
| 2 version tokens set.                     |
+-------------------------------------------+

每种情况下的令牌列表都指定为以分号分隔的 成对列表。生成的令牌列表值导致这些服务器分配: name=value

  • 任何服务器都接受对任一数据库的读取。

  • 只有服务器 2 接受emp 数据库更新。

  • 只有服务器 3 接受 prod数据库更新。

除了为每个服务器分配一个版本令牌列表之外,管理应用程序还维护一个反映服务器分配的缓存。

在与服务器通信之前,客户端应用程序联系管理应用程序并检索有关服务器分配的信息。然后客户端根据这些分配选择服务器。假设客户端想要对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;
+-----------+------------+
| last_name | first_name |
+-----------+------------+
| Smith     | Abe        |
+-----------+------------+
1 row in set (0.01 sec)

服务器和客户端版本令牌列表之间的差异可能以两种方式发生:

只要服务器 2 的分配没有改变,客户端就会继续使用它进行读写。但是假设管理应用程序想要更改服务器分配,以便emp 必须将对数据库的写入发送到服务器 1 而不是服务器 2。为此,它使用 version_tokens_edit()修改emp两个服务器上的令牌值(并更新其服务器缓存作业):

服务器 1:

mysql> SELECT version_tokens_edit('emp=write');
+----------------------------------+
| version_tokens_edit('emp=write') |
+----------------------------------+
| 1 version tokens updated.        |
+----------------------------------+

服务器 2:

mysql> SELECT version_tokens_edit('emp=read');
+---------------------------------+
| version_tokens_edit('emp=read') |
+---------------------------------+
| 1 version tokens updated.       |
+---------------------------------+

version_tokens_edit()修改服务器令牌列表中的命名令牌并保持其他令牌不变。

下次客户端向服务器2发送语句时,自己的token列表不再匹配server token列表,报错:

mysql> UPDATE emp.employee SET salary = salary * 1.1 WHERE id = 4982;
ERROR 3136 (42000): Version token mismatch for emp. Correct value read

在这种情况下,客户端应联系管理应用程序以获取有关服务器分配的更新信息,选择新服务器,并将失败的语句发送到新服务器。

笔记

每个客户端必须通过仅根据它在给定服务器上注册的令牌列表发送语句来与 Version Tokens 合作。例如,如果客户端注册了令牌列表'emp=read',则 Version Tokens 中没有任何内容可以阻止客户端发送emp数据库更新。客户本身必须避免这样做。

对于从客户端接收到的每个语句,服务器隐式地使用锁定,如下所示:

  • 为客户端令牌列表中(即 version_tokens_session 值中) 命名的每个令牌获取共享锁

  • 执行服务器和客户端令牌列表之间的比较

  • 根据比较结果执行语句或产生错误

  • 释放锁

服务器使用共享锁,以便可以在不阻塞的情况下对多个会话进行比较,同时防止在操作服务器令牌列表中的同名令牌之前更改任何试图获取独占锁的会话的令牌。

前面的示例仅使用了 Version Tokens 插件库中包含的几个函数,但还有其他函数。一组函数允许操作和检查服务器的版本标记列表。另一组函数允许锁定和解锁版本令牌。

这些函数允许创建、更改、删​​除和检查服务器的版本令牌列表:

  • version_tokens_set() 完全替换当前列表并分配一个新列表。参数是以分号分隔的 对列表。 name=value

  • version_tokens_edit()启用对当前列表的部分修改。它可以添加新令牌或更改现有令牌的值。参数是以分号分隔的 对列表。 name=value

  • version_tokens_delete() 从当前列表中删除标记。参数是以分号分隔的令牌名称列表。

  • version_tokens_show() 显示当前令牌列表。它不需要争论。

如果成功,这些函数中的每一个都会返回一个二进制字符串,指示发生了什么操作。以下示例建立服务器令牌列表,通过添加新令牌对其进行修改,删除一些令牌,并显示生成的令牌列表:

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=valueversion_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 解释参数如下:

  • 忽略名称和值周围的空格。名称和值中的空格是允许的。(对于 version_tokens_delete(),它采用没有值的名称列表,名称周围的空格将被忽略。)

  • 没有报价机制。

  • 令牌的顺序并不重要,除非令牌列表包含给定令牌名称的多个实例,最后一个值优先于前面的值。

根据这些规则,前面的 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 version tokens set?发生这种情况是因为原始标记列表包含 的两个定义 tok1,并且第二个定义替换了第一个。

Version Tokens 令牌操作函数对令牌名称和值施加了这些约束:

  • 令牌名称不能包含=;字符,并且最大长度为 64 个字符。

  • 令牌值不能包含;字符。值的长度受 max_allowed_packet系统变量值的限制。

  • 版本令牌将令牌名称和值视为二进制字符串,因此比较区分大小写。

Version Tokens 还包括一组功能,可以锁定和解锁令牌:

每个锁定函数返回非零表示成功。否则会报错:

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)'.

使用 Version Tokens 锁定函数进行锁定是建议性的;应用程序必须同意合作。

可以锁定不存在的令牌名称。这不会创建令牌。

笔记

Version Tokens 锁定函数基于第 5.6.9.1 节“锁定服务”中描述的锁定服务,因此对于共享锁和独占锁具有相同的语义。(版本令牌使用服务器内置的锁定服务例程,而不是锁定服务功能接口,因此无需安装这些功能即可使用版本令牌。)版本令牌获取的锁定使用锁定服务名称空间version_token_locks. 可以使用 Performance Schema 监控锁定服务锁,因此 Version Tokens 锁也是如此。具体参见锁服务监控

对于 Version Tokens 锁定函数,完全按照指定使用令牌名称参数。周围的空格不会被忽略,=并且; 允许使用字符。这是因为 Version Tokens 只是将要锁定的令牌名称按原样传递给锁定服务。