Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  ...  /  Stored Function Examples to Manipulate GTIDs

19.1.3.8 使用 GTID 运算符的存储函数示例

本节提供了使用 MySQL 内置函数来与 GTID 基于复制的存储函数示例,列表如下:

  • GTID_SUBSET():判断一个 GTID 集合是否是另一个 GTID 集合的子集。

  • GTID_SUBTRACT():返回一个 GTID 集合中不在另一个 GTID 集合中的 GTID。

  • WAIT_FOR_EXECUTED_GTID_SET():等待指定 GTID 集合中的所有事务都已被执行。

更多关于这些函数的信息,请参见第14.18.2节,“使用全局事务标识符(GTIDs)函数”

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

mysql> delimiter |

本节中的所有存储函数都使用字符串表示的 GTID 集合作为参数,因此 GTID 集合总是需要被引用时使用引号。

这个函数返回两个 GTID 集合是否相同的布尔值,即使它们的格式不同:

CREATE FUNCTION GTID_IS_EQUAL(gs1 LONGTEXT, gs2 LONGTEXT)
  RETURNS INT
  RETURN GTID_SUBSET(gs1, gs2) AND GTID_SUBSET(gs2, gs1)
|

这个函数返回两个 GTID 集合是否不相交的布尔值:

CREATE FUNCTION GTID_IS_DISJOINT(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS INT
  RETURN GTID_SUBSET(gs1, GTID_SUBTRACT(gs1, gs2))
|

这个函数返回两个 GTID 集合的并集:

CREATE FUNCTION GTID_IS_DISJOINT_UNION(gs1 LONGTEXT, gs2 LONGTEXT, sum LONGTEXT)
RETURNS INT
  RETURN GTID_IS_EQUAL(GTID_SUBTRACT(sum, gs1), gs2) AND
         GTID_IS_EQUAL(GTID_SUBTRACT(sum, gs2), gs1)
|

这个函数返回 GTID 集合的 normalized 形式,所有大写,去除空格和重复项,UUID 排序在字母顺序,时间间隔排序在数字顺序:

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

这个函数返回两个 GTID 集合的并集:

CREATE FUNCTION GTID_UNION(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_NORMALIZE(CONCAT(gs1, ',', gs2))
|

这个函数返回两个 GTID 集合的交集。

CREATE FUNCTION GTID_INTERSECTION(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(gs1, GTID_SUBTRACT(gs1, gs2))
|

这个函数返回两个 GTID 集合的对称差集,即在 gs1 中但不在 gs2 中的 GTID,以及在 gs2 中但不在 gs1 中的 GTID。

CREATE FUNCTION GTID_SYMMETRIC_DIFFERENCE(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS LONGTEXT
  RETURN GTID_SUBTRACT(CONCAT(gs1, ',', gs2), GTID_INTERSECTION(gs1, gs2))
|

这个函数从 GTID 集合中删除所有具有指定起源的 GTID,并返回剩余的 GTID。UUID 是服务器中事务的标识符,通常是server_uuid的值。

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

这个函数是上一个函数的反向操作;它返回 GTID 集合中来自指定服务器(UUID)的所有 GTID。

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

示例 19.1:验证复制副本是否最新

内置函数GTID_SUBSET()GTID_SUBTRACT()可以用来检查副本是否至少应用了源服务器中的每个事务。

使用 GTID_SUBSET() 函数在副本上执行以下语句:

SELECT GTID_SUBSET(source_gtid_executed, replica_gtid_executed);

如果返回值是 0(false),这意味着 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,源服务器已经应用了些事务,但复制服务器还没有应用,这意味着复制服务器不最新。


示例 19.2:备份和恢复场景

存储函数 GTID_IS_EQUAL()GTID_IS_DISJOINT()GTID_IS_DISJOINT_UNION() 可以用来验证涉及多个数据库和服务器的备份和恢复操作。在这个示例场景中,server1 包含数据库 db1server2 包含数据库 db2。目标是将数据库 db2 复制到 server1,并在 server1 上的结果应该是这两个数据库的并集。用于实现的步骤是将 server2 使用 mysqldump 进行备份,然后将备份恢复到 server1 上。

假设 mysqldump 使用 --set-gtid-purged 设置为 ONAUTO(默认),输出包含 SET @@GLOBAL.gtid_purged 语句,该语句将 gtid_executed 集合从 server2 添加到 gtid_purged 集合中 server1 上。 gtid_purged 包含了在给定服务器上已提交的事务,但这些事务在服务器上的任何二进制日志文件中都不存在。当将数据库 db2 复制到 server1 时,需要将 server2 上未在 server1 的二进制日志文件中的事务的 GTID 添加到 gtid_purged 中,以使 server1 的集成完整。

这些存储函数可以帮助完成以下步骤在这个场景中:

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

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

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

  • 使用 GTID_IS_DISJOINT() 验证 mysqldump 输出中的 GTID 集合是否与 gtid_executed 集合在 server1 上没有重叠。出现相同的 GTIDs 在两个服务器上将导致在将 db2 数据库从 server2 复制到 server1 时出现错误。检查方法是在 server1 上提取并存储 gtid_purged 输出到本地变量中,然后执行以下语句:

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

    如果结果为 1,两个 GTID 集合之间没有重叠,因此没有重复的 GTIDs。

  • 使用 GTID_IS_DISJOINT_UNION() 验证恢复操作是否在 server1 上正确地设置了 GTID 状态。在 server1 上执行以下语句之前,提取现有的 gtid_executed 集合:

    server1> SELECT @@GLOBAL.gtid_executed;

    将结果存储到本地变量 $original_gtid_executed,并将 gtid_purged 集合存储到另一个本地变量中,如前所述。将 server2 的备份从 server2 复制到 server1,然后执行以下语句验证 GTID 状态:

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

    如果结果为 1,存储函数验证了 server1 上的原始 gtid_executed 集合 ($original_gtid_executed) 和从 server2 复制的 gtid_purged 集合 ($gtid_purged_set) 之间没有重叠,并且 server1 上的更新 gtid_executed 集合现在由 server1 上的原始 gtid_executed 集合和 server2 上的 gtid_purged 集合组成,这是所需的结果。确保在 server1 上执行该检查之前,不要在 server1 上执行任何其他事务,否则将导致错误。


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

存储函数GTID_UNION()可以用来确定一组副本中的最新副本,以便在源服务器意外停止后执行手动故障转移操作。如果一些副本正在经历延迟复制,这个存储函数可以用来计算最新副本,而不需要等待所有副本应用现有日志记录,从而最小化故障转移时间。该函数可以返回每个副本的gtid_executed的并集,以及副本接收到的事务记录,这些记录在性能chema的replication_connection_status表中。您可以将这些结果进行比较,以找到哪个副本的记录是最新的,即使不是所有事务都已经提交了。

在每个副本上,计算事务记录的完整记录,可以使用以下语句:

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

然后,您可以比较每个副本的结果,以找到哪个副本拥有最新的事务记录,并使用该副本作为新的源。


Example 19.4 检查副本上的额外事务

存储函数GTID_SUBTRACT_UUID()可以用来检查副本是否接收到了不来自其指定源或源的交易。如果是,那么可能存在复制设置或代理、路由器或负载均衡器的问题。这函数通过从指定的源服务器中删除GTID集中的所有GTID,并返回剩余的GTID,如果有。

对于具有单个源的副本,使用以下语句,给出源的标识符,这通常是server_uuid

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

如果结果不是空的,那么返回的交易是额外的交易,它们不来自指定的源。

对于多源拓扑结构中的副本,包括每个源的服务器UUID在函数调用中,如下所示:

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

如果结果不是空的,那么返回的交易是额外的交易,它们不来自任何指定的源。


Example 19.5 验证拓扑结构中的服务器是只读的

存储函数GTID_INTERSECTION_WITH_UUID()可以用来验证服务器是否没有生成任何GTID并处于只读状态。该函数返回来自指定标识符的服务器的GTID集中的那些GTID。 如果在gtid_executed中列出的任何事务使用了服务器自己的标识符,那么服务器自己生成了这些事务。您可以在服务器上使用以下语句来检查:

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

Example 19.6 验证多源复制中的额外副本

存储函数GTID_INTERSECTION_WITH_UUID()可以用来确定一个多源复制设置中的某个源是否已经应用了来自该源的所有事务。在这个场景中,source1source2都是源和复制服务器,并且相互复制。source2还拥有自己的复制服务器。该复制服务器也可以从source1接收和应用事务,但只有在source2配置了log_replica_updates=ON时才会这样做;否则,它不会这样做。无论哪种情况,我们当前想要确定复制服务器是否已经与source2同步。在这种情况下,可以使用GTID_INTERSECTION_WITH_UUID()来确定source2生成的交易,忽略source2source1复制的交易。然后,可以使用GTID_SUBSET()来比较结果与复制服务器上的gtid_executed集。如果复制服务器已经与source2同步,那么复制服务器上的gtid_executed集包含了交集集中的所有事务(来自source2的交易)。

要执行这个检查,首先将gtid_executed和服务器UUID从source2和复制服务器上的gtid_executed值存储到用户变量中:

source2> SELECT @@GLOBAL.gtid_executed INTO @source2_gtid_executed;

source2> SELECT @@GLOBAL.server_uuid INTO @source2_server_uuid;

replica> SELECT @@GLOBAL.gtid_executed INTO @replica_gtid_executed;

然后使用GTID_INTERSECTION_WITH_UUID()GTID_SUBSET(),将这些变量作为输入,如下所示:

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

source2获取的服务器标识符(@source2_server_uuid)与GTID_INTERSECTION_WITH_UUID()一起使用,以确定和返回来自source2的GTID集,忽略来自source1的GTID。然后,使用GTID_SUBSET()将结果与复制服务器上的gtid_executed集进行比较。如果该语句返回非零(true),那么来自source2的所有标识的GTID(第一个输入集)也可以在复制服务器上的gtid_executed集中找到,这意味着复制服务器已经接收和执行了来自source2的所有事务。