Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


7.6.6.3 使用 版本令牌

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

Version Tokens 可以在以下场景中有用:在需要对 MySQL 服务器进行负载均衡管理的系统中,可以监控服务器并根据负载变化调整服务器分配。该系统包括以下组件:

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

  • 一个管理或管理应用程序,它与服务器通信,并将其组织到高可用组中。组的作用不同,每个组中的服务器可能有不同的分配。某个组中的服务器分配可以随时更改。

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

Version Tokens 允许根据分配管理服务器访问,而不需要客户端反复查询服务器的分配:

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

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

  • 为了提高性能,客户端应用程序从管理应用程序获取缓存信息,从而避免了对服务器分配的查询。基于它所发送的语句类型(例如,读取或写入),客户端选择合适的服务器并连接到它。

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

    另一方面,可能管理应用程序已经更改了服务器分配和版本令牌列表。在这种情况下,新的服务器分配可能现在与客户端要求不兼容。服务器和客户端令牌列表之间的令牌不匹配,服务器返回语句的错误。这是客户端刷新版本令牌信息从管理应用程序缓存,并选择新的服务器以通信的提示。

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

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

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

以下示例展示了前面讨论的内容。

当Version Tokens在给定的服务器上初始化时,服务器的版本令牌列表为空。令牌列表维护是通过调用函数来实现的。需要调用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,并在服务器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的分配不变,客户端将继续使用它进行读写。但是,管理应用程序想要将写操作发送到服务器1,而不是服务器2,以便将写操作发送到服务器1。为了实现这个目标,它使用version_tokens_edit()来修改服务器1和服务器2上的令牌值(并更新其缓存的服务器分配):

服务器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时,它的令牌列表不再与服务器令牌列表匹配,出现错误:

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

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

Note

每个客户端都必须与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;               |
+-----------------------+

如果令牌列表 malformed 发生警告:

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

  • 名称和值周围的空格被忽略。名称和值中的空格被允许。 (对于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 将令牌名称和值视为二进制字符串,因此比较是区分大小写的。

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 锁定函数是建议的;应用程序必须同意合作。

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

Note

Version Tokens 锁定函数基于第7.6.9.1节,“锁定服务”中的锁定服务,具有相同的共享和排他锁定语义。 (Version Tokens 使用服务器内置的锁定服务子程序,而不是锁定服务函数接口,因此无需安装锁定服务函数以使用 Version Tokens。) Version Tokens 锁定使用的锁定服务命名空间为version_token_locks。锁定服务锁可以使用性能架构来监控,因此 Version Tokens 锁定也可以这样做。详见锁定服务监控

对于 Version Tokens 锁定函数,令牌名称参数将被精确地使用。周围的空格不被忽略,并且=;字符被允许。这是因为 Version Tokens 直接将要锁定的令牌名称传递给锁定服务。