在使用版本令牌之前,请根据第 7.6.6.2 节“安装或卸载版本令牌”中的说明进行安装。
版本令牌可以在以下场景中发挥作用:系统访问一组 MySQL 服务器,但需要根据负载变化对服务器进行管理,以便进行负载均衡。该系统由以下元素组成:
-
要管理的一组 MySQL 服务器。
-
一个管理或管理应用程序,它与服务器通信,并将它们组织成高可用性组。组件可以有不同的目的,服务器在每个组中的分配可能会改变。
-
客户端应用程序,它们访问服务器以检索和更新数据,根据服务器的分配选择服务器。例如,客户端不应该将更新发送到只读服务器。
版本令牌允许根据分配管理服务器访问,而不需要客户端重复查询服务器关于其分配的信息:
-
管理应用程序执行服务器分配,并在每个服务器上建立版本令牌以反映其分配。应用程序缓存该信息,以提供中央访问点。
如果管理应用程序需要更改服务器分配(例如,从允许写入更改为只读),它将更改服务器的版本令牌列表,并更新缓存。
-
为了提高性能,客户端应用程序从管理应用程序获取缓存信息,从而避免了为每个语句检索服务器分配信息。基于客户端发出的语句类型(例如,读取 vs 写入),客户端选择合适的服务器并连接到它。
-
此外,客户端将自己的客户端特定版本令牌发送到服务器,以注册所需的服务器分配。对于客户端发送到服务器的每个语句,服务器将其自己的令牌列表与客户端令牌列表进行比较。如果服务器令牌列表包含客户端令牌列表中的所有令牌且值相同,则匹配成功,服务器执行语句。
否则,可能是管理应用程序已经更改了服务器分配及其版本令牌列表。在这种情况下,新的服务器分配可能与客户端要求不兼容。服务器和客户端令牌列表之间出现令牌不匹配,服务器返回错误以响应语句。这是客户端刷新版本令牌信息的指示,从管理应用程序缓存中获取新的服务器分配信息,并选择新的服务器进行通信。
客户端逻辑可以以不同的方式实现版本令牌错误检测和服务器选择:
-
客户端可以自己处理所有版本令牌注册、不匹配检测和连接切换。
-
逻辑可以在连接器中实现,该连接器管理客户端和 MySQL 服务器之间的连接。该连接器可能自己处理不匹配错误检测和语句重新发送,或者将错误传递给应用程序,以便应用程序重新发送语句。
以下示例更具体地说明了上述讨论。
当版本令牌在给定服务器上初始化时,服务器的版本令牌列表为空。令牌列表维护是通过调用函数来实现的。VERSION_TOKEN_ADMIN
权限(或已弃用的 SUPER
权限)是调用任何版本令牌函数所需的,因此令牌列表修改预计将由具有该权限的管理或管理应用程序执行。
假设管理应用程序与一组服务器通信,这些服务器由客户端查询以访问员工和产品数据库(分别命名为 emp
和 prod
)。所有服务器都允许处理数据检索语句,但只有其中一些服务器允许进行数据库更新。为了在数据库特定基础上处理此问题,管理应用程序在每个服务器上建立了版本令牌列表。在服务器的令牌列表中,令牌名称表示数据库名称,令牌值是 read
或 write
,取决于数据库是否必须以只读方式使用或可以进行读取和写入。
客户端应用程序通过设置系统变量注册它们所需的版本令牌列表。变量设置是在客户端特定基础上进行的,因此不同的客户端可以注册不同的要求。默认情况下,客户端令牌列表为空,这将匹配任何服务器令牌列表。当客户端将其令牌列表设置为非空值时,匹配可能成功或失败,取决于服务器版本令牌列表。
要定义服务器的版本令牌列表,管理应用程序调用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. |
+-------------------------------------------+
每个情况下的令牌列表都是以分号分隔的
对指定的。结果令牌列表值导致这些服务器分配:名称
=值
-
任何服务器都接受来自任一数据库的读取。
-
只有服务器 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)
服务器和客户端版本令牌列表之间的不一致可能以两种方式发生:
-
客户端
version_tokens_session
值中的令牌名称不在服务器令牌列表中。在这种情况下,会发生ER_VTOKEN_PLUGIN_TOKEN_NOT_FOUND
错误。 -
客户端
version_tokens_session
值中的令牌值与服务器令牌列表中的相应令牌值不同。在这种情况下,会发生ER_VTOKEN_PLUGIN_TOKEN_MISMATCH
错误。
只要服务器 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 时,其自己的令牌列表不再与服务器令牌列表匹配,会发生错误:
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'
,则版本令牌中没有什么可以阻止客户端发送 emp
数据库的更新。客户端本身必须避免这样做。
对于从客户端接收的每个语句,服务器隐式使用锁定,如下所示:
-
为客户端令牌列表中的每个令牌名称获取共享锁
-
比较服务器和客户端令牌列表
-
根据比较结果执行语句或产生错误
-
释放锁
服务器使用共享锁,以便多个会话可以同时比较,而不阻止任何会话在服务器令牌列表中操作同名令牌之前获取独占锁。
前面的示例仅使用了 Version Tokens 插件库中的少数函数,但还有其他函数。其中一组函数允许服务器的版本令牌列表被操作和检查。另一组函数允许版本令牌被锁定和解锁。
这些函数允许服务器的版本令牌列表被创建、更改、删除和检查:
-
version_tokens_set()
完全替换当前列表并分配新列表。参数是一个以分号分隔的
对列表。名称
=值
-
version_tokens_edit()
启用当前列表的部分修改。它可以添加新令牌或更改现有令牌的值。参数是一个以分号分隔的
对列表。名称
=值
-
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)
如前所述,版本令牌使用以分号分隔的
对列表定义。考虑以下 名称
=值
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 还包括一组函数,用于锁定和解锁令牌:
-
version_tokens_lock_exclusive()
获取独占版本令牌锁。它需要一个或多个锁名称列表和超时值。 -
version_tokens_lock_shared()
获取共享版本令牌锁。它需要一个或多个锁名称列表和超时值。 -
version_tokens_unlock()
释放版本令牌锁(独占和共享)。它不需要参数。
每个锁定函数返回非零值以指示成功。否则,出现错误:
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 锁定函数的锁定是咨询的;应用程序必须合作。
可以锁定不存在的令牌名称。这不会创建令牌。
版本令牌锁定函数基于第 7.6.9.1 节,“锁定服务”中描述的锁定服务,因此具有相同的共享和独占锁语义。(版本令牌使用服务器内置的锁定服务例程,而不是锁定服务函数接口,因此不需要安装锁定服务函数来使用版本令牌。)由版本令牌获取的锁使用锁定服务命名空间 version_token_locks
。锁定服务锁可以使用性能架构进行监控,因此版本令牌锁也可以这样做。详细信息,请参阅 锁定服务监控。
对于版本令牌锁定函数,令牌名称参数将被严格使用。周围的空白字符不会被忽略,=
和 ;
字符是允许的。这是因为版本令牌只是将要锁定的令牌名称原样传递给锁定服务。