在使用版本令牌之前,请根据第5.6.6.2节“安装或卸载版本令牌”中提供的说明进行 安装。
使用版本令牌的情况是,系统访问一个MySQL服务器集合,但需要通过监视它们并根据负载变化调整服务器分配来管理它们以实现负载平衡。这样的系统包括以下组件:
- 要管理的MySQL服务器的集合。
- 与服务器通信并将其组织为高可用性组的管理或管理应用程序。组具有不同的用途,每个组中的服务器可能具有不同的分配。特定组中服务器的分配可以随时更改。
- 访问服务器以检索和更新数据的客户端应用程序,根据分配给它们的目的选择服务器。例如,客户端不应将更新发送到只读服务器。
版本令牌允许根据分配来管理服务器访问,而无需客户端重复查询服务器有关其分配的信息:
-
管理应用程序执行服务器分配,并在每个服务器上建立版本令牌以反映其分配。应用程序将缓存此信息,以为其提供中央访问点。
如果某个时候管理应用程序需要更改服务器分配(例如,将其从允许写入更改为只读),它将更改服务器的版本令牌列表并更新其缓存。
-
为了提高性能,客户端应用程序从管理应用程序获取缓存信息,从而使它们避免必须检索有关每个语句的服务器分配的信息。根据它将发出的语句类型(例如,读取还是写入),客户端选择适当的服务器并连接到该服务器。
-
另外,客户端向服务器发送自己的客户端特定版本令牌,以注册其所需的服务器分配。对于客户端发送给服务器的每个语句,服务器会将自己的令牌列表与客户端令牌列表进行比较。如果服务器令牌列表包含客户端令牌列表中存在的所有令牌,并且具有相同的值,则存在匹配项,服务器将执行该语句。
另一方面,也许管理应用程序已更改服务器分配及其版本令牌列表。在这种情况下,新的服务器分配现在可能与客户端要求不兼容。服务器和客户端令牌列表之间的令牌不匹配,服务器返回错误以回复该语句。这指示客户端从管理应用程序缓存中刷新其版本令牌信息,并选择要与之通信的新服务器。
用于检测版本令牌错误和选择新服务器的客户端逻辑可以通过以下方式实现:
- 客户端可以自行处理所有版本令牌注册,不匹配检测和连接切换。
- 这些动作的逻辑可以在管理客户端和MySQL服务器之间的连接的连接器中实现。这样的连接器可能会处理不匹配错误检测并重新发送自身的语句,也可能会将错误传递给应用程序,然后将其留给应用程序以重新发送该语句。
下面的示例以更具体的形式说明了前面的讨论。
当版本令牌在给定服务器上初始化时,该服务器的版本令牌列表为空。令牌列表维护通过调用用户定义函数(UDF)来执行。该 VERSION_TOKEN_ADMIN特权(或已经过时的SUPER特权)来调用任何版本令牌的UDF,所以令牌列表修改预期由具有相应权限的管理或行政程序来完成。
假设管理应用程序与客户端查询的一组服务器进行通信,以访问员工和产品数据库(分别命名为emp和 prod)。允许所有服务器处理数据检索语句,但只允许其中一些进行数据库更新。为了在特定于数据库的基础上处理此问题,管理应用程序在每个服务器上建立一个版本令牌的列表。在给定服务器的令牌列表中,令牌名称表示数据库名称,令牌值 取决于read或write取决于是否必须以只读方式使用数据库或是否可以进行读写操作。
客户端应用程序通过设置系统变量来注册它们要求服务器匹配的版本令牌列表。变量设置是基于特定于客户端的,因此不同的客户端可以注册不同的需求。默认情况下,客户端令牌列表为空,与任何服务器令牌列表匹配。当客户端将其令牌列表设置为非空值时,匹配可能成功还是失败,具体取决于服务器版本的令牌列表。
要定义服务器的版本令牌列表,管理应用程序将调用 version_tokens_set()UDF。(还有用于修改和显示令牌列表的UDF,如后所述。)例如,应用程序可能会将这些语句发送到三个服务器组成的一组:
服务器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)
服务器和客户端版本令牌列表之间的差异可能通过两种方式发生:
- 该
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_session值)中 命名的每个令牌获取共享锁 - 执行服务器和客户端令牌列表之间的比较
- 根据比较结果执行语句或产生错误
- 释放锁
服务器使用共享锁,因此可以在不阻塞的情况下进行多个会话的比较,同时防止任何在尝试操作服务器令牌列表中相同名称的令牌之前尝试获取排他锁的会话的令牌更改。
前面的示例仅使用Version Tokens插件库中包含的一些用户定义,但还有其他一些。一组UDF允许操作和检查服务器的版本令牌列表。另一组UDF允许锁定和解锁版本令牌。
这些UDF允许创建,更改,删除和检查服务器的版本令牌列表:
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*=*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_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,而第二个定义替换了第一个定义。
版本令牌令牌操作UDF将这些约束放在令牌名称和值上:
- 令牌名称不能包含
=或个;字符,并且最大长度为64个字符。 - 令牌值不能包含
;字符。值的长度受max_allowed_packet系统变量的值约束 。 - 版本令牌将令牌名称和值视为二进制字符串,因此比较区分大小写。
版本令牌还包括一组UDF,这些UDF使令牌可以被锁定和解锁:
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)'.
建议使用“版本令牌”锁定功能进行锁定。申请必须同意合作。
可以锁定不存在的令牌名称。这不会创建令牌。
注意
版本令牌锁定功能基于第5.6.8.1节“锁定服务”中描述的锁定服务,因此对于共享锁定和互斥锁定具有相同的语义。(版本令牌使用服务器中内置的锁定服务例程,而不使用锁定服务UDF接口,因此无需安装这些UDF即可使用版本令牌。)版本令牌获得的锁使用的锁定服务命名空间 version_token_locks。可以使用性能模式监视锁定服务锁,因此对于版本令牌锁也是如此。有关详细信息,请参阅 锁定服务监视。
对于版本令牌锁定功能,令牌名称参数的使用与指定的完全相同。不会忽略周围的空格,=并且; 允许使用字符。这是因为版本令牌只是将要锁定的令牌名称照原样传递给锁定服务。




