Documentation Home
MySQL 8.0 参考手册  / 第十七章复制  / 17.1 配置复制  / 17.1.3 使用全局事务标识符进行复制  /  16.1.3.7 操作 GTID 的存储函数示例

16.1.3.7 操作 GTID 的存储函数示例

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

GTID_SUBSET(set1,set2)

给定两组全局事务标识符 set1set2,如果所有 GTIDset1也在 中 ,则返回 true set2。否则返回假。

GTID_SUBTRACT(set1,set2)

给定两组全局事务标识符 set1set2,仅返回那些不在set1中 的 GTID set2

WAIT_FOR_EXECUTED_GTID_SET(gtid_set[, timeout])

等到服务器应用了全局事务标识符包含在 中的所有事务 gtid_set。可选的超时会在指定的秒数过去后停止函数等待。

WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(gtid_set[, timeout][,channel])

类似WAIT_FOR_EXECUTED_GTID_SET(),但对于单个启动的复制通道。改为使用 WAIT_FOR_EXECUTED_GTID_SET()以确保在所有状态下覆盖所有频道。

有关这些函数的详细信息,请参阅 第 12.19 节,“与全局事务标识符 (GTID) 一起使用的函数”

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

注意,在这些存储函数中,已经使用delimiter命令将MySQL语句分隔符改为竖线,如下:

mysql> delimiter |

所有这些函数都将 GTID 集的字符串表示形式作为参数,因此在与它们一起使用时必须始终引用 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 按字母顺序排列,间隔按数字顺序排列。

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,以及存在于但不 gtid_set_2存在的GTID 。 gtid_set_2gtid_set_1

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 (false),则 中的某些 GTID source_gtid_executed不存在于 中replica_gtid_executed,因此源应用了一些副本未应用的事务,因此副本不是最新的。

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

SELECT GTID_SUBTRACT(source_gtid_executed, replica_gtid_executed)

此语句返回任何在 source_gtid_executed但不在中的 GTID replica_gtid_executed。如果返回任何 GTID,则源应用了一些副本未应用的事务,因此副本不是最新的。


示例 16.2 备份和恢复场景

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

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

存储的函数可用于协助执行此场景中的以下步骤:

  • 用于GTID_IS_EQUAL验证备份操作是否为 SET @@GLOBAL.gtid_purged语句计算了正确的 GTID 集。在 上 ,从mysqlpumpmysqldumpserver2输出中提取该语句 ,并将 GTID 集存储到局部变量中,例如. 然后执行如下语句: $gtid_purged_set

    server2> SELECT GTID_IS_EQUAL($gtid_purged_set, @@GLOBAL.gtid_executed);

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

  • 用于GTID_IS_DISJOINT验证mysqlpumpmysqldumpgtid_executed输出中设置的 GTID 是否与 上的设置 重叠server1。如果有任何重叠,由于某种原因在两台服务器上都存在相同的 GTID,则复制数据库db2server1产生错误。如上所述,要检查、on server1、提取 gtid_purged集合并将其从输出中存储到局部变量中,然后执行以下语句:

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

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

  • 用于GTID_IS_DISJOINT_UNION验证还原操作是否在 上产生了正确的 GTID 状态server1。在恢复备份之前,在 上,通过执行以下语句 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,则存储函数已验证来自 ( ) 的原始 集合gtid_executed 和来自server1 ( $original_gtid_executed) gtid_purged添加的集合没有重叠,并且现在更新 的集合 由先前的 集合 from 加上来自的 集合组成 ,这是期望的结果。确保在 上进行任何进一步交易之前执行此检查,否则集合中的新交易会 导致失败。 server2$gtid_purged_setgtid_executedserver1gtid_executedserver1gtid_purgedserver2server1gtid_executed


示例 16.3 为手动故障转移选择最新的副本

存储函数GTID_UNION可用于从一组副本中识别最新的副本,以便在复制源服务器意外停止后执行手动故障转移操作。如果某些副本遇到复制延迟,则可以使用此存储函数计算最新的副本,而无需等待所有副本应用其现有的中继日志,从而最大限度地减少故障转移时间。该函数可以返回 gtid_executed每个副本上的集合与副本接收到的交易集合的并集,该集合记录在性能模式表中 replication_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 可用于检查副本是否已收到并非源自其指定来源的交易。如果有,则可能是您的复制设置或代理、路由器或负载平衡器有问题。此函数的工作原理是从 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也有自己的复制品。source1副本还接收并应用来自ifsource2配置为的 事务,但如果使用 log_slave_updates=ON则它不会这样做。不管怎样,我们目前只想知道副本是否是最新的。在这种情况下,存储函数 source2log_slave_updates=OFFsource2GTID_INTERSECTION_WITH_UUID可用于识别source2 起源的事务,丢弃 source2从复制 的事务source1GTID_SUBSET然后可以使用内置函数 将结果gtid_executed与副本上的集合进行比较。如果副本是最新的 source2gtid_executed则副本上的集合包含交集中的所有事务(源自 的事务 source2)。

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

    $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_UUIDand GTID_SUBSETwith 这些变量作为输入,如下:

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

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