5.5.5.3 使用版本令牌

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

使用版本令牌的情况是,系统访问一个 MySQL 服务器集合,但需要通过监视它们并根据负载变化调整服务器分配来 Management 它们以实现负载平衡。这样的系统包括以下组件:

  • 要 Management 的 MySQL 服务器的集合。

  • 与服务器通信并将其组织为高可用性组的 Management 或 Management 应用程序。组具有不同的用途,每个组中的服务器可能具有不同的分配。特定组中服务器的分配可以随时更改。

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

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

  • Management 应用程序执行服务器分配,并在每个服务器上构建版本令牌以反映其分配。应用程序将缓存此信息,以为其提供中央访问点。

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

  • 为了提高性能,Client 端应用程序从 Management 应用程序获取缓存信息,从而使它们避免必须检索有关每个语句的服务器分配的信息。根据它将发出的语句类型(例如,读取还是写入),Client 端选择适当的服务器并连接到该服务器。

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

另一方面,也许 Management 应用程序已更改服务器分配及其版本令牌列 table。在这种情况下,新的服务器分配现在可能与 Client 端要求不兼容。服务器和 Client 端令牌列 table 之间的令牌不匹配,服务器返回错误以回复该语句。这指示 Client 端从 Management 应用程序缓存中刷新其版本令牌信息,并选择要与之通信的新服务器。

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

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

  • 这些动作的逻辑可以在 ManagementClient 端和 MySQL 服务器之间的连接的连接器中实现。这样的连接器可能会处理不匹配错误检测并重新发送自身的语句,或者可能会将错误传递给应用程序,然后将其留给应用程序以重新发送该语句。

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

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

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

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

要定义服务器的版本令牌列 table,Management 应用程序将调用version_tokens_set() UDF。 (还有用于修改和显示令牌列 table 的 UDF,如后所述.)例如,应用程序可能会将这些语句发送到三个服务器组成的一组:

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.                     |
+-------------------------------------------+

在每种情况下,令牌列 table 都指定为以name=value对的分号分隔的列 table。结果令牌列 table 值将导致以下服务器分配:

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

  • 仅服务器 2 接受emp数据库的更新。

  • 仅服务器 3 接受prod数据库的更新。

除了为每个服务器分配版本令牌列 table 以外,Management 应用程序还维护反映服务器分配的缓存。

在与服务器通信之前,Client 端应用程序联系 Management 应用程序并检索有关服务器分配的信息。然后,Client 端根据这些分配选择服务器。假设 Client 端要在emp数据库上执行读取和写入操作。根据前面的分配,只有服务器 2 合格。Client 端连接到服务器 2 并通过设置其version_tokens_session系统变量在其中注册其服务器要求:

mysql> SET @@SESSION.version_tokens_session = 'emp=write';

对于 Client 端发送到服务器 2 的后续语句,服务器将自己的版本令牌列 table 与 Client 端列 table 进行比较,以检查它们是否匹配。如果是这样,则语句将正常执行:

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)

服务器和 Client 端版本令牌列 table 之间的差异可能通过两种方式发生:

只要服务器 2 的分配没有更改,Client 端就会 continue 使用它进行读取和写入。但是,假设 Management 应用程序想要更改服务器分配,以便必须将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()修改服务器令牌列 table 中的命名令牌,并使其他令牌保持不变。

Client 端下一次向服务器 2 发送一条语句时,其自己的令牌列 table 不再与服务器令牌列 table 匹配,并且发生错误:

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

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

Note

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

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

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

  • 执行服务器和 Client 端令牌列 table 之间的比较

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

  • 释放锁

服务器使用共享锁,因此可以在不阻塞的情况下进行多个会话的比较,同时防止任何在尝试操作服务器令牌列 table 中相同名称的令牌之前尝试获取排他锁的会话的令牌更改。

前面的示例仅使用 Version Tokens 插件库中包含的一些用户定义,但还有其他一些。一组 UDF 允许操作和检查服务器的版本令牌列 table。另一组 UDF 允许锁定和解锁版本令牌。

这些 UDF 允许创建,更改,删除和检查服务器的版本令牌列 table:

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

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

  • version_tokens_delete()从当前列 table 中删除令牌。该参数是用分号分隔的令牌名称列 table。

  • version_tokens_show()显示当前令牌列 table。不用 Arguments。

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

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;               |
+-----------------------+

如果令牌列 table 格式错误,则会出现警告:

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对列 table 定义的。考虑对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(),它使用没有值的名称列 table,名称周围的空格将被忽略。)

  • 没有报价机制。

  • 令牌的 Sequences 无关紧要,除非令牌列 table 包含给定令牌名称的多个实例,最后一个值优先于较早的值。

给定这些规则,前面的version_tokens_set()调用会生成带有两个令牌的令牌列 table:tok1的值为1'2 3"4tok2的值为a = b。要验证这一点,请致电version_tokens_show()

mysql> SELECT version_tokens_show();
+--------------------------+
| version_tokens_show()    |
+--------------------------+
| tok2=a = b;tok1=1'2 3"4; |
+--------------------------+

如果令牌列 table 包含两个令牌,为什么version_tokens_set()返回值3 version tokens set?发生这种情况是因为原始令牌列 table 包含tok1的两个定义,而第二个定义替换了第一个。

版本令牌令牌操作 UDF 将这些约束放在令牌名称和值上:

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

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

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

版本令牌还包括一组 UDF,使令牌可以被锁定和解锁:

每个锁定函数都返回非零值以 table 示成功。否则,将发生错误:

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

建议使用“版本令牌”锁定功能进行锁定。申请必须同意合作。

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

Note

版本令牌锁定功能基于第 28.3.1 节“锁定服务”中描述的锁定服务,因此对于共享锁定和互斥锁定具有相同的语义。 (版本令牌使用服务器中内置的锁定服务例程,而不使用锁定服务 UDF 接口,因此不需要安装这些 UDF 即可使用版本令牌.)版本令牌获得的锁定使用version_token_locks锁定服务命名空间。可以使用性能模式监视锁定服务锁,因此对于版本令牌锁也是如此。有关详细信息,请参见第 28.3.1.2.3 节“锁定服务监视”

对于版本令牌锁定功能,令牌名称参数的使用与指定的完全相同。不会忽略周围的空格,并且允许使用=;个字符。这是因为版本令牌只是将要锁定的令牌名称照原样传递给锁定服务。