#### 17.1.3.8 操作 GTID 的存储函数示例

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

`GTID_SUBSET(*`设置1`*,*`设置2`*)`

给定两组全局事务标识符*`设置1`*和*`设置2`*, 如果所有 GTID 在*`设置1`*也在*`设置2`*.否则返回 false。

`GTID_SUBTRACT(*`设置1`*,*`设置2`*)`

给定两组全局事务标识符*`设置1`*和*`设置2`*, 只返回那些 GTID*`设置1`*不在*`设置2`*.

`WAIT_FOR_EXECUTED_GTID_SET(*`gtid_set`*[, *`暂停`*])`

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

有关这些功能的详细信息，请参阅[第 12.19 节，“与全局事务标识符 (GTID) 一起使用的函数”](gtid-functions.html).

您可以定义自己的存储函数来使用 GTID。有关定义存储函数的信息，请参阅[第 25 章，*存储对象*](stored-objects.html).下面的例子展示了一些有用的存储函数，这些函数可以基于内置的`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 集不相交，则此函数返回非零 (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_set_2`，以及存在于`gtid_set_2`但不在`gtid_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`](replication-options.html#sysvar_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))|
```

[](<>)

**示例 17.1 验证副本是否是最新的**

内置函数`GTID_SUBSET`和`GTID_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`*但不在*`replica_gtid_executed`*.如果返回任何 GTID，则源应用了副本未应用的一些事务，因此副本不是最新的。

[](<>)

**示例 17.2 备份和恢复方案**

存储的函数`GTID_IS_EQUAL`,`GTID_IS_DISJOINT`， 和`GTID_IS_DISJOINT_UNION`可用于验证涉及多个数据库和服务器的备份和恢复操作。在此示例场景中，`服务器1`包含数据库`db1`， 和`服务器2`包含数据库`数据库2`.目标是复制数据库`数据库2`到`服务器1`, 结果在`服务器1`应该是两个数据库的联合。使用的程序是备份`服务器2`使用[**mysql泵**](mysqlpump.html)要么[**mysql转储**](mysqldump.html)，然后在`服务器1`.

提供备份程序的选项`--set-gtid-purge`被设置为`在`或默认`汽车`，程序的输出包含一个`SET @@GLOBAL.gtid_purged`声明增加了[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)从设置`服务器2`到[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)出发`服务器1`.这[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)set 包含已在服务器上提交但不存在于服务器上的任何二进制日志文件中的所有事务的 GTID。当数据库`数据库2`被复制到`服务器1`, 提交的事务的 GTID`服务器2`，它们不在二进制日志文件中`服务器1`, 必须添加到`服务器1`的[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)设置以使设置完成。

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

-   采用`GTID_IS_EQUAL`验证备份操作是否计算了正确的 GTID 集`SET @@GLOBAL.gtid_purged`陈述。在`服务器2`，从[**mysql泵**](mysqlpump.html)要么[**mysql转储**](mysqldump.html)输出，并将 GTID 集存储到局部变量中，例如`$gtid_purged_set`.然后执行以下语句：

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

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

-   采用`GTID_IS_DISJOINT`验证 GTID 设置在[**mysql泵**](mysqlpump.html)要么[**mysql转储**](mysqldump.html)输出不与[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)出发`服务器1`.两台服务器上存在相同的 GTID 会导致复制数据库时出错`数据库2`到`服务器1`.要检查，在`服务器1`，提取并存储[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)如上所述将输出设置为局部变量，然后执行以下语句：

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

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

-   采用`GTID_IS_DISJOINT_UNION`验证恢复操作是否导致正确的 GTID 状态`服务器1`.在恢复备份之前，打开`服务器1`，获取现有的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)通过执行以下语句设置：

    ```
    server1> SELECT @@GLOBAL.gtid_executed;
    ```

    将结果存储在局部变量中`$original_gtid_executed`.同时存储[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)如上所述设置在局部变量中。当备份从`服务器2`已恢复到`服务器1`，执行以下语句验证 GTID 状态：

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

    如果结果为 1，则存储函数已验证原始[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)从设置`服务器1`(`$original_gtid_executed`) 和[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)从添加的集合`服务器2`(`$gtid_purged_set`) 没有重叠，而且更新后的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)出发`服务器1`现在包括以前的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)从设置`服务器1`加上[`gtid_purged`](replication-options-gtids.html#sysvar_gtid_purged)从设置`服务器2`，这是想要的结果。确保在任何进一步的交易发生之前进行此检查`服务器1`, 否则新交易[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)设置导致它失败。

[](<>)

**示例 17.3 选择最新的副本进行手动故障转移**

存储的函数`GTID_UNION`可用于从一组副本中识别最新的副本，以便在源服务器意外停止后执行手动故障转移操作。如果某些副本出现复制延迟，则可以使用此存储函数来计算最新的副本，而无需等待所有副本应用其现有的中继日志，从而最大限度地减少故障转移时间。该函数可以返回并集[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)在每个副本上设置副本接收到的事务集，记录在 Performance Schema 表中[`复制连接状态`](performance-schema-replication-connection-status-table.html).即使尚未提交所有事务，您也可以比较这些结果以找出哪个副本的事务记录是最新的。

在每个副本上，通过发出以下语句来计算交易的完整记录：

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

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

[](<>)

**示例 17.4 检查副本上的无关事务**

存储的函数`GTID_SUBTRACT_UUID`可用于检查副本是否接收到并非源自其指定源的事务。如果有，您的复制设置或代理、路由器或负载平衡器可能存在问题。此函数通过从 GTID 集中删除来自指定源服务器的所有 GTID 并返回剩余的 GTID（如果有）来工作。

对于从单个源复制的副本，发出以下语句，给出原始源的标识符，通常是[`server_uuid`](replication-options.html#sysvar_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);
```

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

[](<>)

**示例 17.5 验证复制拓扑中的服务器是否为只读**

存储的函数`GTID_INTERSECTION_WITH_UUID`可以用来验证服务器没有发起任何 GTID 并且处于只读状态。该函数仅返回 GTID 集中源自具有指定标识符的服务器的那些 GTID。如果服务器中的任何事务[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)设置有服务器自己的标识符，服务器本身发起了这些事务。您可以在服务器上发出以下语句进行检查：

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

[](<>)

**示例 17.6 验证多源复制设置中的其他副本**

存储的函数`GTID_INTERSECTION_WITH_UUID`可用于查明附加到多源复制设置的副本是否已应用源自一个特定源的所有事务。在这种情况下，`来源1`和`来源2`既是源又是副本，并且相互复制。`来源2`也有自己的复制品。副本还从源接收和应用事务`来源1`如果`来源2`配置有[`log_replica_updates=ON`](replication-options-binary-log.html#sysvar_log_replica_updates)要么[`log_slave_updates=ON`](replication-options-binary-log.html#sysvar_log_slave_updates), 但如果`来源2`用途[`log_replica_updates=OFF`](replication-options-binary-log.html#sysvar_log_replica_updates)要么[`log_slave_updates=OFF`](replication-options-binary-log.html#sysvar_log_slave_updates).无论如何，我们目前只想知道副本是否是最新的`来源2`.在这种情况下，存储函数`GTID_INTERSECTION_WITH_UUID`可用于识别交易`来源2`发起，丢弃交易`来源2`已复制自`来源1`.内置函数`GTID_SUBSET`然后可用于将结果与[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)在副本上设置。如果副本是最新的`来源2`， 这[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)副本上的集合包含交集中的所有事务（源自`来源2`）。

要执行此检查，请存储`来源2`的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)放，`来源2`的服务器 UUID 和副本的[`gtid_executed`](replication-options-gtids.html#sysvar_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_UUID`和`GTID_SUBSET`将这些变量作为输入，如下所示：

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

服务器标识符来自`来源2`(`$source2_server_uuid`) 与`GTID_INTERSECTION_WITH_UUID`识别并仅返回那些 GTID`来源2`的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)起源于`来源2`, 省略那些起源于`来源1`.然后将生成的 GTID 集与副本上所有已执行 GTID 的集进行比较，使用`GTID_SUBSET`.如果此语句返回非零 (true)，则所有已识别的 GTID 来自`来源2`（第一组输入）也在副本的[`gtid_executed`](replication-options-gtids.html#sysvar_gtid_executed)set（第二组输入），意味着副本已经复制了所有源自的事务`来源2`.
