16.1.3.7 存储的用于操纵 GTID 的函数示例

MySQL 包含一些内置(本机)函数,可用于基于 GTID 的复制。这些功能如下:

  • GTID_SUBSET(set1,set2)

    • 给定两组全局事务标识符* set1 set2 ,如果 set1 中的所有 GTID 也在 set2 *中,则返回 true。否则返回 false。
  • GTID_SUBTRACT(set1,set2)

    • 给定两组全局事务标识符* set1 set2 ,仅返回 set1 中不在 set2 *中的那些 GTID。
  • WAIT_FOR_EXECUTED_GTID_SET(gtid_set[, timeout])

    • await 服务器应用所有其全局事务标识符包含在* gtid_set *中的事务。在指定的秒数过去之后,可选的超时使功能停止 await。
  • WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(gtid_set[, timeout][,channel])

    • 类似于WAIT_FOR_EXECUTED_GTID_SET(),,但用于单个已启动的复制通道。请使用WAIT_FOR_EXECUTED_GTID_SET()来确保所有状态下的所有 Channels 均被覆盖。

有关这些功能的详细信息,请参见第 12.18 节“与全局事务标识符一起使用的函数”

您可以定义自己的存储函数以使用 GTID。有关定义存储功能的信息,请参见第 23 章,存储的对象。以下示例显示了一些有用的存储函数,这些函数可以基于内置的GTID_SUBSET()GTID_SUBTRACT()函数创建。

请注意,在这些存储的函数中,已使用 delimiter 命令将 MySQL 语句的定界符更改为竖线,如下所示:

mysql> delimiter |

所有这些函数都将 GTID 集的字符串 table 示形式用作参数,因此 GTID 集与它们一起使用时必须始终用引号引起来。

如果两个 GTID 集是同一集合,即使它们的格式不同,此函数也会返回非零(true)。

CREATE FUNCTION GTID_IS_EQUAL(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT)
RETURNS INT
  RETURN GTID_SUBSET(gtid_set_1, gtid_set_2) AND GTID_SUBSET(gtid_set_2, gtid_set_1)|

如果两个 GTID 集不相交,则此函数返回非零(true)。

CREATE FUNCTION GTID_IS_DISJOINT(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT)
RETURNS INT
  RETURN GTID_SUBSET(gtid_set_1, GTID_SUBTRACT(gtid_set_1, gtid_set_2))|

如果两个 GTID 集不相交,并且sum是这两个集的并集,则此函数返回非零(true)。

CREATE FUNCTION GTID_IS_DISJOINT_UNION(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT, sum LONGTEXT)
RETURNS INT
  RETURN GTID_IS_EQUAL(GTID_SUBTRACT(sum, gtid_set_1), gtid_set_2) AND
         GTID_IS_EQUAL(GTID_SUBTRACT(sum, gtid_set_2), gtid_set_1)|

此函数以大写形式返回 GTID 集的规范化形式,没有空格,也没有重复项。 UUID 以字母 Sequences 排列,间隔以数字 Sequences 排列。

CREATE FUNCTION GTID_NORMALIZE(g LONGTEXT)
RETURNS LONGTEXT
RETURN GTID_SUBTRACT(g, '')|

此函数返回两个 GTID 集的并集。

CREATE FUNCTION GTID_UNION(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_NORMALIZE(CONCAT(gtid_set_1, ',', gtid_set_2))|

此函数返回两个 GTID 集的交集。

CREATE FUNCTION GTID_INTERSECTION(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(gtid_set_1, GTID_SUBTRACT(gtid_set_1, gtid_set_2))|

此函数返回两个 GTID 集之间的对称差异,即,存在于gtid_set_1而不存在于gtid_set_2中的 GTID,以及存在于gtid_set_2而不存在于gtid_set_1中的 GTID。

CREATE FUNCTION GTID_SYMMETRIC_DIFFERENCE(gtid_set_1 LONGTEXT, gtid_set_2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(CONCAT(gtid_set_1, ',', gtid_set_2), GTID_INTERSECTION(gtid_set_1, gtid_set_2))|

该函数从 GTID 集合中删除指定来源的所有 GTID,并返回剩余的 GTID(如果有)。 UUID 是发起事务的服务器使用的标识符,通常为server_uuid值。

CREATE FUNCTION GTID_SUBTRACT_UUID(gtid_set LONGTEXT, uuid TEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(gtid_set, CONCAT(UUID, ':1-', (1 << 63) - 2))|

此函数将先前列出的函数反向,以仅返回 GTID 集中来自具有指定标识符(UUID)的服务器的 GTID。

CREATE FUNCTION GTID_INTERSECTION_WITH_UUID(gtid_set LONGTEXT, uuid TEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(gtid_set, GTID_SUBTRACT_UUID(gtid_set, uuid))|

例 16.1 验证副本是否为最新

内置函数GTID_SUBSETGTID_SUBTRACT可用于检查副本是否已至少应用了源已应用的每个事务。

要使用GTID_SUBSET执行此检查,请在副本上执行以下语句:

SELECT GTID_SUBSET(source_gtid_executed, replica_gtid_executed)

如果返回 0(否),则* source_gtid_executed 中的某些 GTID 在 replica_gtid_executed *中不存在,因此源已应用了某些未应用副本的事务,因此该副本不是最新的。

要使用GTID_SUBTRACT执行检查,请在副本上执行以下语句:

SELECT GTID_SUBTRACT(source_gtid_executed, replica_gtid_executed)

该语句返回* source_gtid_executed 中的所有 GTID,而不是 replica_gtid_executed *中的所有 GTID。如果返回了任何 GTID,则源已应用了某些副本未应用的事务,因此该副本不是最新的。

例 16.2 备份和还原方案

存储的函数GTID_IS_EQUALGTID_IS_DISJOINTGTID_IS_DISJOINT_UNION可用于验证涉及多个数据库和服务器的备份和还原操作。在此示例场景中,server1包含数据库db1,而server2包含数据库db2。目标是将数据库db2复制到server1,并且server1上的结果应该是两个数据库的并集。所使用的过程是使用mysqlpumpmysqldump备份server2,然后在server1上还原此备份。

如果备份程序的选项--set-gtid-purged设置为ON或默认值AUTO,则该程序的输出包含SET @@GLOBAL.gtid_purged语句,该语句会将server2中的gtid_executed集添加到server1上的gtid_purged集。 gtid_purged集包含已在服务器上提交但在服务器上的任何二进制日志文件中都不存在的所有事务的 GTID。将数据库db2复制到server1时,必须将在server2上提交的事务的 GTID(不在server1的二进制日志文件中)添加到server1gtid_purged集中,以使该集完整。

在这种情况下,存储的功能可用于协助执行以下步骤:

  • 使用GTID_IS_EQUAL验证备份操作为SET @@GLOBAL.gtid_purged语句计算出正确的 GTID 集。在server2上,从mysqlpumpmysqldump输出中提取该语句,并将 GTID 集存储到本地变量中,例如$gtid_purged_set。然后执行以下语句:
server2> SELECT GTID_IS_EQUAL($gtid_purged_set, @@GLOBAL.gtid_executed);

如果结果为 1,则两个 GTID 集相等,并且该集已正确计算。

  • 使用GTID_IS_DISJOINT来验证在mysqlpumpmysqldump输出中设置的 GTID 与在server1上设置的gtid_executed不重叠。如果存在任何重叠,并且由于某些原因两个服务器上都存在相同的 GTID,则在将数据库db2复制到server1时会看到错误。要进行检查,在server1上,从输出中提取gtid_purged集并将其存储到如上所述的局部变量中,然后执行以下语句:
server1> SELECT GTID_IS_DISJOINT($gtid_purged_set, @@GLOBAL.gtid_executed);

如果结果为 1,则两个 GTID 集之间没有重叠,因此不存在重复的 GTID。

  • 使用GTID_IS_DISJOINT_UNION验证还原操作是否在server1上导致正确的 GTID 状态。在还原备份之前,在server1上,通过执行以下语句来获取现有的gtid_executed集:
server1> SELECT @@GLOBAL.gtid_executed;

将结果存储在局部变量$original_gtid_executed中。如上所述,还将gtid_purged集存储在本地变量中。将server2的备份还原到server1后,执行以下语句来验证 GTID 状态:

server1> SELECT GTID_IS_DISJOINT_UNION($original_gtid_executed,
$gtid_purged_set,
@@GLOBAL.gtid_executed);

如果结果为 1,则存储的函数已验证server1($original_gtid_executed)中的原始gtid_executed集和server2($gtid_purged_set)中添加的gtid_purged集没有重叠,并且server1上的已更新gtid_executed集现在包括server1之前的gtid_executed设置加上server2gtid_purged设置,这是所需的结果。确保在server1上进行任何其他事务之前执行此检查,否则gtid_executed集中的新事务将导致它失败。

例 16.3 选择用于手动故障转移的最新副本

存储的函数GTID_UNION可用于从一组副本中标识最新副本,以便在复制源服务器意外停止后执行手动故障转移操作。如果某些副本遇到复制滞后,则可以使用此存储的功能来计算最新的副本,而无需 await 所有副本都应用其现有中继日志,因此可以最大程度地缩短故障转移时间。该函数可以返回每个副本上的gtid_executed集与副本所接收的事务集的并集,并记录在性能模式 tablereplication_connection_status中。您可以比较这些结果,以找出哪个副本的事务记录是最新的,即使尚未提交所有事务也是如此。

在每个副本上,通过发出以下语句来计算事务的完整记录:

SELECT GTID_UNION(RECEIVED_TRANSACTION_SET, @@GLOBAL.gtid_executed)
 FROM performance_schema.replication_connection_status
 WHERE channel_name = 'name';

然后,您可以比较每个副本的结果,以查看哪个副本具有最新的事务记录,并将此从属服务器用作新的复制源服务器。

例 16.4 检查副本上的无关事务

所存储的函数GTID_SUBTRACT_UUID可用于检查副本是否已接收到不是源自其指定来源的事务。如果存在,则复制设置或代理,Router 或负载平衡器可能存在问题。通过从 GTID 集中删除来自指定原始服务器的所有 GTID,并返回剩余的 GTID(如果有),可以使用此功能。

对于具有单个源的副本,请发出以下语句,并提供原始源的标识符,通常为server_uuid值:

SELECT GTID_SUBTRACT_UUID(@@GLOBAL.gtid_executed, server_uuid_of_source);

如果结果不为空,则返回的事务是不是源自指定来源的额外事务。

对于多源复制拓扑中的副本,请重复该功能,例如:

SELECT GTID_SUBTRACT_UUID(GTID_SUBTRACT_UUID(@@GLOBAL.gtid_executed,
                        server_uuid_of_source_1),
                         server_uuid_of_source_2);

如果结果不为空,则返回的事务是不是源自任何指定来源的额外事务。

示例 16.5 验证复制拓扑中的服务器是只读的

存储的函数GTID_INTERSECTION_WITH_UUID可用于验证服务器未发起任何 GTID 并处于只读状态。该函数仅从 GTID 集中返回源自具有指定标识符的服务器的 GTID。如果服务器的gtid_executed集中的任何事务都有服务器自己的标识符,则服务器本身将发起这些事务。您可以在服务器上发出以下语句进行检查:

SELECT GTID_INTERSECTION_WITH_UUID(@@GLOBAL.gtid_executed, my_server_uuid);

示例 16.6 在多源复制设置中验证其他副本

存储的函数GTID_INTERSECTION_WITH_UUID可用于确定附加到多源复制设置的副本是否已应用了源自一个特定源的所有事务。在这种情况下source1source2都是源和副本,并且彼此复制。 source2也有自己的副本。如果将source2配置为log_slave_updates=ON,则副本还将接收并应用source1的事务,但是如果source2使用log_slave_updates=OFF则副本不会这样做。无论如何,我们目前仅想确定副本是否为source2最新。在这种情况下,存储的函数GTID_INTERSECTION_WITH_UUID可用于标识source2发起的事务,而丢弃source2已从source1复制的事务。然后可以使用内置函数GTID_SUBSET将结果与副本上设置的gtid_executed进行比较。如果副本的最新信息是source2,那么副本上的gtid_executed集将包含交集中的所有事务(源自source2的事务)。

要执行此检查,请将source2gtid_executed集,source2的服务器 UUID 和副本的gtid_executed集存储到 Client 端变量中,如下所示:

$source2_gtid_executed :=
      source2> SELECT @@GLOBAL.gtid_executed;
    $source2_server_uuid :=
      source2> SELECT @@GLOBAL.server_uuid;
    $replica_gtid_executed :=
      replica> SELECT @@GLOBAL.gtid_executed;

然后使用GTID_INTERSECTION_WITH_UUIDGTID_SUBSET并将这些变量作为 Importing,如下所示:

SELECT GTID_SUBSET(GTID_INTERSECTION_WITH_UUID($source2_gtid_executed,
                                               $source2_server_uuid),
                                               $replica_gtid_executed);

source2($source2_server_uuid)中的服务器标识符与GTID_INTERSECTION_WITH_UUID一起使用,以仅标识和返回source2gtid_executed集合中源自source2的 GTID,而忽略那些源自source1的 GTID。然后使用GTID_SUBSET将生成的 GTID 集与副本上所有已执行 GTID 的集进行比较。如果此语句返回非零(true),则从source2(第一个集合 Importing)识别的所有 GTID 也在副本的gtid_executed集合(第二个集合 Importing)中,这意味着副本已复制了source2起源的所有事务。