#### 17.1.7.3 跳过交易

如果复制由于复制事务中的事件问题而停止，您可以通过跳过副本上的失败事务来恢复复制。在跳过事务之前，请确保复制 I/O（接收方）线程以及 SQL（应用程序）线程已停止。

首先，您需要识别导致错误的复制事件。错误的详细信息和最后一次成功应用的事务记录在 Performance Schema 表中[`replication_applier_status_by_worker`](performance-schema-replication-applier-status-by-worker-table.html).您可以使用[**mysqlbinlog**](mysqlbinlog.html)检索并显示在错误发生时记录的事件。有关执行此操作的说明，请参阅[第 7.5 节，“时间点（增量）恢复”](point-in-time-recovery.html).或者，您可以发出[`显示中继日志事件`](show-relaylog-events.html)在复制品上或[`显示 BINLOG 事件`](show-binlog-events.html)在源头上。

在跳过事务并重新启动副本之前，请检查以下几点：

-   是否停止了来自未知或不受信任来源的复制的事务？如果是这样，请调查原因，以防有任何安全考虑表明不应重新启动副本。

-   停止复制的事务是否需要应用到副本上？如果是这样，请进行适当的更正并重新应用事务，或者手动协调副本上的数据。

-   停止复制的事务是否需要在源上应用？如果没有，请在最初发生的服务器上手动撤消事务。

    要跳过事务，请根据需要选择以下方法之一：

-   使用 GTID 时 ([`gtid_mode`](replication-options-gtids.html#sysvar_gtid_mode)是`在`）， 看[第 17.1.7.3.1 节，“使用 GTID 跳过事务”](replication-administration-skip.html#replication-administration-skip-gtid).

-   当 GTID 未使用或正在分阶段使用时 ([`gtid_mode`](replication-options-gtids.html#sysvar_gtid_mode)是`离开`,`OFF_PERMISSIVE`， 要么`ON_PERMISSIVE`）， 看[第 17.1.7.3.2 节，“跳过没有 GTID 的事务”](replication-administration-skip.html#replication-administration-skip-nogtid).

-   如果您已使用复制通道启用 GTID 分配`ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS`的选项[`将复制源更改为`](change-replication-source-to.html)要么[`将主更改为`](change-master-to.html)声明，见[第 17.1.7.3.2 节，“跳过没有 GTID 的事务”](replication-administration-skip.html#replication-administration-skip-nogtid).使用`ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS`在复制通道上与为通道引入基于 GTID 的复制不同，您不能对这些通道使用基于 GTID 的复制的事务跳过方法。

    要在跳过事务后重新启动复制，请发出[`开始复制`](start-replica.html), 与`频道`子句如果副本是多源副本。

##### 17.1.7.3.1 使用 GTID 跳过事务

使用 GTID 时 ([`gtid_mode`](replication-options-gtids.html#sysvar_gtid_mode)是`在`)，即使事务的内容被过滤掉，已提交事务的 GTID 也会保留在副本上。此功能可防止副本在使用 GTID 自动定位重新连接到源时检索先前过滤的事务。它还可以用于跳过副本上的事务，方法是提交一个空事务来代替失败的事务。

当您使用复制通道启用 GTID 分配时，这种跳过事务的方法不适合`ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS`的选项[`将复制源更改为`](change-replication-source-to.html)陈述。

如果失败的事务在工作线程中产生错误，您可以直接从`APPLYING_TRANSACTION`性能架构表中的字段[`replication_applier_status_by_worker`](performance-schema-replication-applier-status-by-worker-table.html).要查看交易是什么，请发出[`显示中继日志事件`](show-relaylog-events.html)在复制品上或[`显示 BINLOG 事件`](show-binlog-events.html)在源上，并在输出中搜索以该 GTID 开头的事务。

当您按照前面描述的任何其他适当操作（例如安全考虑）评估失败事务时，要跳过它，请在与失败事务具有相同 GTID 的副本上提交一个空事务。例如：

```
SET GTID_NEXT='aaa-bbb-ccc-ddd:N';
BEGIN;
COMMIT;
SET GTID_NEXT='AUTOMATIC';
```

副本上这个空事务的存在意味着当你发出一个[`开始复制`](start-replica.html)语句重新启动复制，副本使用自动跳过功能忽略失败的事务，因为它看到具有该 GTID 的事务已被应用。如果副本是多源副本，则提交空事务时不需要指定通道名，但发出时需要指定通道名[`开始复制`](start-replica.html).

请注意，如果在此副本上使用二进制日志记录，则如果副本将来成为源或主副本，则空事务将进入复制流。如果您需要避免这种可能性，请考虑刷新和清除副本的二进制日志，如下例所示：

```
FLUSH LOGS;
PURGE BINARY LOGS TO 'binlog.000146';
```

空事务的 GTID 被保留，但事务本身通过清除二进制日志文件被删除。

##### 17.1.7.3.2 跳过没有 GTID 的事务

在 GTID 未使用或正在分阶段使用时跳过失败的事务 ([`gtid_mode`](replication-options-gtids.html#sysvar_gtid_mode)是`离开`,`OFF_PERMISSIVE`， 要么`ON_PERMISSIVE`)，您可以通过发出`设置全局 sql_replica_skip_counter`语句（来自 MySQL 8.0.26）或`设置全局 sql_slave_skip_counter`陈述。或者，您可以通过发出一个事件跳过一个或多个事件[`将复制源更改为`](change-replication-source-to.html)要么[`将主更改为`](change-master-to.html)语句将源二进制日志位置向前移动。

当您使用复制通道启用 GTID 分配时，这些方法也适用`ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS`的选项[`将复制源更改为`](change-replication-source-to.html)要么[`将主更改为`](change-master-to.html)陈述。

当您使用这些方法时，重要的是要了解您不一定要跳过完整的事务，就像前面描述的基于 GTID 的方法一样。这些非基于 GTID 的方法不知道事务本身，而是对事件进行操作。二进制日志被组织为一系列称为事件组的组，每个事件组由一系列事件组成。

-   对于事务表，一个事件组对应一个事务。

-   对于非事务性表，一个事件组对应一个 SQL 语句。

    单个事务可以包含对事务表和非事务表的更改。

    当您使用`设置全局 sql_replica_skip_counter`要么`设置全局 sql_slave_skip_counter`语句跳过事件并且结果位置在事件组的中间，副本继续跳过事件，直到它到达组的末尾。然后从下一个事件组开始执行。这[`将复制源更改为`](change-replication-source-to.html)要么[`将主更改为`](change-master-to.html)语句没有此功能，因此您必须小心确定正确的位置以在事件组的开头重新启动复制。但是，使用[`将复制源更改为`](change-replication-source-to.html)要么[`将主更改为`](change-master-to.html)意味着您不必计算需要跳过的事件，就像您使用`设置全局 sql_replica_skip_counter`要么`设置全局 sql_slave_skip_counter`，而您可以只指定重新启动的位置。

###### 17.1.7.3.2.1 使用 \`SET GLOBAL 跳过事务

sql_slave_skip_counter\`

[](<>)[](<>)[](<>)[](<>)

当您按照前面描述的任何其他适当的操作（例如安全注意事项）评估失败的事务时，计算您需要跳过的事件数。一个事件通常对应二进制日志中的一条 SQL 语句，但请注意，使用`自动递增`要么`LAST_INSERT_ID()`在二进制日志中计为两个事件。当使用二进制日志事务压缩时，压缩的事务有效负载 (`Transaction_payload_event`) 被计为单个计数器值，因此其中的所有事件都作为一个单元跳过。

如果要跳过完整的事务，可以将事件计数到事务结束，也可以直接跳过相关的事件组。请记住，与`设置全局 sql_replica_skip_counter`要么`设置全局 sql_slave_skip_counter`，副本继续跳到事件组的末尾。确保不要向前跳过太远并进入下一个事件组或事务，以免它也被跳过。

发出`放`声明如下，其中*`ñ`*是要跳过的源中的事件数：

```
SET GLOBAL sql_slave_skip_counter = N

Or from MySQL 8.0.26:
SET GLOBAL sql_replica_skip_counter = N
```

如果出现以下情况，则不能发布本声明：[`gtid_模式=开启`](replication-options-gtids.html#sysvar_gtid_mode)或者如果复制I/O（接收器）和SQL（应用程序）线程正在运行。

这个`设置全局sql\u副本\u跳过\u计数器`或`设置全局sql_slave_skip_计数器`声明没有立即生效。当你发布[`开始复制`](start-replica.html)下一次的声明`设置`语句，系统变量的新值[`sql_复制_跳过_计数器`](replication-options-replica.html#sysvar_sql_replica_skip_counter)或[`sql_slave_skip_计数器`](replication-options-replica.html#sysvar_sql_slave_skip_counter)应用，并跳过事件。那个[`开始复制`](start-replica.html)语句还自动将系统变量的值设置回0。如果复制副本是多源复制副本，则在发出[`开始复制`](start-replica.html)声明`频道`条款是必需的。确保您指定了正确的频道，否则将跳过错误频道上的事件。

###### 17.1.7.3.2.2跳过与`将主机更改为`

当您评估了失败的事务是否执行了前面所述的任何其他适当操作（例如安全注意事项）时，请在源二进制日志中标识坐标（文件和位置），这些坐标表示重新启动复制的适当位置。这可以是导致问题的事件之后事件组的开始，也可以是下一个事务的开始。复制I/O（接收器）线程在下一次线程启动时开始从这些坐标处的源读取数据，跳过失败事件。确保您准确地确定了位置，因为此声明未考虑事件组。

发布[`将复制源更改为`](change-replication-source-to.html)或[`将主机更改为`](change-master-to.html)声明如下，其中*`源\日志\名称`*是包含重新启动位置的二进制日志文件，以及*`来源_日志_位置`*表示二进制日志文件中所述的重启位置的数字：

```
CHANGE MASTER TO MASTER_LOG_FILE='source_log_name', MASTER_LOG_POS=source_log_pos;

Or from MySQL 8.0.24:
CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='source_log_name', SOURCE_LOG_POS=source_log_pos;
```

如果复制副本是多源复制副本，则必须使用`频道`子句来命名[`将复制源更改为`](change-replication-source-to.html)或[`将主机更改为`](change-master-to.html)陈述

如果出现以下情况，则不能发布本声明：`SOURCE_AUTO_POSITION=1`或`主自动位置=1`或者如果复制I/O（接收器）和SQL（应用程序）线程正在运行。如果需要在以下情况下使用这种跳过事务的方法：`SOURCE_AUTO_POSITION=1`或`主自动位置=1`正常情况下，您可以将设置更改为`SOURCE_AUTO_POSITION=0`或`主自动位置=0`在发布声明时，然后再次更改。例如：

```
CHANGE MASTER TO MASTER_AUTO_POSITION=0, MASTER_LOG_FILE='binlog.000145', MASTER_LOG_POS=235;
CHANGE MASTER TO MASTER_AUTO_POSITION=1;

Or from MySQL 8.0.24:

CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION=0, SOURCE_LOG_FILE='binlog.000145', SOURCE_LOG_POS=235;
CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION=1;
```
