本节提供了一些存储函数的示例(参见第 27 章, 存储对象),您可以使用 MySQL 为使用基于 GTID 的复制提供的内置函数创建这些函数,这些函数列在下面
GTID_SUBSET()
: 显示一个 GTID 集是否是另一个 GTID 集的子集。GTID_SUBTRACT()
: 返回一个 GTID 集中不在另一个 GTID 集中的 GTID。WAIT_FOR_EXECUTED_GTID_SET()
: 等待直到给定 GTID 集中的所有事务都已执行。
参见第 14.18.2 节, “与全局事务标识符 (GTID) 一起使用的函数”, 了解有关这些函数的更多信息。
请注意,在这些存储函数中,分隔符命令已用于将 MySQL 语句分隔符更改为竖线,如下所示
mysql> delimiter |
本节中显示的所有存储函数都以 GTID 集的字符串表示形式作为参数,因此在与它们一起使用时,GTID 集必须始终加引号。
如果两个 GTID 集是相同的集合,则此函数返回非零值(true),即使它们不是以相同方式格式化。
CREATE FUNCTION GTID_IS_EQUAL(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS INT
RETURN GTID_SUBSET(gs1, gs2) AND GTID_SUBSET(gs2, gs1)
|
如果两个 GTID 集是不相交的,则此函数返回非零值(true)。
CREATE FUNCTION GTID_IS_DISJOINT(gs1 LONGTEXT, gs2 LONGTEXT)
RETURNS INT
RETURN GTID_SUBSET(gs1, GTID_SUBTRACT(gs1, gs2))
|
如果两个 GTID 集是不相交的,并且 sum
是它们的并集,则此函数返回非零值(true)。
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 集的规范形式,全部大写,无空格,无重复,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
中但不在 replica_gtid_executed
中的任何 GTID。如果返回了任何 GTID,则表明源已应用了副本尚未应用的一些事务,因此副本不是最新的。
示例 19.2 备份和还原场景
存储函数 GTID_IS_EQUAL()
、GTID_IS_DISJOINT()
和 GTID_IS_DISJOINT_UNION()
可用于验证涉及多个数据库和服务器的备份和还原操作。在本示例场景中,server1
包含数据库 db1
,而 server2
包含数据库 db2
。目标是将数据库 db2
复制到 server1
,并且 server1
上的结果应是两个数据库的并集。使用的过程是使用 mysqldump 备份 server2
,然后将此备份还原到 server1
。
假设 mysqldump 在运行时将 --set-gtid-purged
设置为 ON
或 AUTO
(默认值),则输出将包含一个 SET @@GLOBAL.gtid_purged
语句,该语句将 gtid_executed
集从 server2
添加到 server1
上的 gtid_purged
集。 gtid_purged
包含在给定服务器上已提交的所有事务的 GTID,但这些事务在服务器上的任何二进制日志文件中都不存在。当数据库 db2
复制到 server1
时,必须将 server2
上已提交的事务的 GTID(不在 server1
上的二进制日志文件中)添加到 server1
的 gtid_purged
中,以使该集完整。
存储函数可用于帮助完成本场景中的以下步骤
使用
GTID_IS_EQUAL()
验证备份操作是否为SET @@GLOBAL.gtid_purged
语句计算了正确的 GTID 集。在server2
上,从 mysqldump 输出中提取该语句,并将 GTID 集存储到本地变量中,例如$gtid_purged_set
。然后执行以下语句server2> SELECT GTID_IS_EQUAL($gtid_purged_set, @@GLOBAL.gtid_executed);
如果结果为 1,则两个 GTID 集相等,并且该集已正确计算。
使用
GTID_IS_DISJOINT()
验证 mysqldump 输出中的 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
的原始gtid_executed
集 ($original_gtid_executed
) 和从server2
添加的gtid_purged
集 ($gtid_purged_set
) 没有重叠,并且server1
上更新的gtid_executed
集现在由server1
的先前gtid_executed
集加上server2
的gtid_purged
集组成,这是预期结果。确保在server1
上发生任何进一步的事务之前执行此检查,否则gtid_executed
中的新事务会导致它失败。
示例 19.3 选择最新的副本以进行手动故障转移
存储函数 GTID_UNION()
可用于从一组副本中识别最新的副本,以便在源服务器意外停止后执行手动故障转移操作。如果某些副本遇到复制延迟,则可以使用此存储函数来计算最新的副本,而无需等待所有副本应用其现有的中继日志,因此可以最大限度地缩短故障转移时间。该函数可以返回每个副本上 gtid_executed
与副本收到的事务集的并集,这些事务集记录在 Performance Schema replication_connection_status
表中。您可以比较这些结果以找出哪个副本的事务记录是最新的,即使并非所有事务都已提交。
在每个副本上,通过发出以下语句来计算完整的事务记录
SELECT GTID_UNION(RECEIVED_TRANSACTION_SET, @@GLOBAL.gtid_executed)
FROM performance_schema.replication_connection_status
WHERE channel_name = 'name';
然后,您可以比较每个副本的结果以查看哪个副本具有最新的事务记录,并将此副本用作新的源。
示例 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);
如果结果不为空,则返回的事务是未从任何指定源发出的额外事务。
示例 19.5 验证复制拓扑中的服务器是否为只读
存储函数 GTID_INTERSECTION_WITH_UUID()
可用于验证服务器是否未发出任何 GTID 且处于只读状态。该函数仅返回来自 GTID 集的那些 GTID,这些 GTID 来自具有指定标识符的服务器。如果此服务器的 gtid_executed
中列出的任何事务使用服务器自己的标识符,则表示服务器本身发出了这些事务。您可以在服务器上发出以下语句进行检查
SELECT GTID_INTERSECTION_WITH_UUID(@@GLOBAL.gtid_executed, my_server_uuid);
示例 19.6 验证多源复制中的额外副本
存储函数 GTID_INTERSECTION_WITH_UUID()
可用于找出附加到多源复制设置的副本是否已应用来自一个特定源的所有事务。在这种情况下,source1
和 source2
都是源和副本,并且相互复制。 source2
也有自己的副本。如果 source2
配置为使用 log_replica_updates=ON
,则该副本也会接收和应用来自 source1
的事务,但如果 source2
使用 log_replica_updates=OFF
,则不会这样做。无论哪种情况,我们目前只想知道该副本是否与 source2
同步。在这种情况下,可以使用 GTID_INTERSECTION_WITH_UUID()
来识别 source2
发出的事务,并丢弃 source2
从 source1
复制的事务。然后可以使用内置函数 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()
一起使用,以识别并仅返回来自该集合的那些 GTID,这些 GTID 来自 source2
,省略来自 source1
的那些 GTID。然后使用 GTID_SUBSET()
将生成的 GTID 集与副本上所有已执行的 GTID 集进行比较。如果此语句返回非零(true),则来自 source2
的所有已识别 GTID(第一个输入集)也存在于副本的 gtid_executed
中,这意味着该副本已接收并执行了来自 source2
的所有事务。