#### 17.5.1.34 复制和事务不一致

根据您的复制配置，可能会出现从中继日志执行的事务顺序不一致的情况。本节说明如何避免不一致并解决它们引起的任何问题。

可能存在以下类型的不一致：

-   *半应用事务*.更新非事务表的事务已应用了部分但不是全部更改。

-   *差距*.当给定一个有序的事务序列时，当在该序列中较晚的事务在该序列中在前的某个其他事务之前应用时，就会出现外部化事务集中的间隙。只有在使用多线程副本时才会出现间隙。

    为避免在多线程副本上出现间隙，请设置[`replica_preserve_commit_order=ON`](replication-options-replica.html#sysvar_replica_preserve_commit_order)（来自 MySQL 8.0.26）或[`slave_preserve_commit_order=ON`](replication-options-replica.html#sysvar_slave_preserve_commit_order)（在 MySQL 8.0.26 之前）。从 MySQL 8.0.27 开始，此设置是默认设置，因为从该版本默认所有副本都是多线程的。

    直到并包括 MySQL 8.0.18，保留提交顺序需要二进制日志 ([`log_bin`](replication-options-binary-log.html#sysvar_log_bin)) 和副本更新日志记录 ([`log_replica_updates`](replication-options-binary-log.html#sysvar_log_replica_updates)要么[`log_slave_updates`](replication-options-binary-log.html#sysvar_log_slave_updates)) 也被启用，这是 MySQL 8.0 的默认设置。从 MySQL 8.0.19 开始，不需要在副本上设置二进制日志记录和副本更新日志记录[`replica_preserve_commit_order=ON`](replication-options-replica.html#sysvar_replica_preserve_commit_order)要么[`slave_preserve_commit_order=ON`](replication-options-replica.html#sysvar_slave_preserve_commit_order)，并且可以根据需要禁用。

    在所有版本中，设置[`replica_preserve_commit_order=ON`](replication-options-replica.html#sysvar_replica_preserve_commit_order)要么[`slave_preserve_commit_order=ON`](replication-options-replica.html#sysvar_slave_preserve_commit_order)要求[`副本并行类型`](replication-options-replica.html#sysvar_replica_parallel_type)（来自 MySQL 8.0.26）或[`slave_parallel_type`](replication-options-replica.html#sysvar_slave_parallel_type)（在 MySQL 8.0.26 之前）设置为`LOGICAL_CLOCK`.从 MySQL 8.0.27（但不适用于早期版本），这是默认设置。

    在某些特定情况下，如描述中所列[`replica_preserve_commit_order`](replication-options-replica.html#sysvar_replica_preserve_commit_order)和[`slave_preserve_commit_order`](replication-options-replica.html#sysvar_slave_preserve_commit_order)， 环境[`replica_preserve_commit_order=ON`](replication-options-replica.html#sysvar_replica_preserve_commit_order)要么[`slave_preserve_commit_order=ON`](replication-options-replica.html#sysvar_slave_preserve_commit_order)无法保留副本上的提交顺序，因此在这些情况下，从副本的中继日志执行的事务序列中可能仍会出现间隙。

    环境`replica_preserve_commit_order=ON`要么`slave_preserve_commit_order=ON`不防止源二进制日志位置滞后。

-   *源二进制日志位置滞后*.即使没有差距，之后的交易也有可能`Exec_master_log_pos`已应用。也就是说，所有交易到点`ñ`已申请，之后没有交易`ñ`已申请，但`Exec_master_log_pos`有一个小于`ñ`.在这个情况下，`Exec_master_log_pos`是应用交易的“低水位线”，落后于最近应用交易的位置。这只能发生在多线程副本上。启用[`replica_preserve_commit_order`](replication-options-replica.html#sysvar_replica_preserve_commit_order)要么[`slave_preserve_commit_order`](replication-options-replica.html#sysvar_slave_preserve_commit_order)不防止源二进制日志位置滞后。

    以下场景与半应用事务、间隙和源二进制日志位置滞后的存在有关：

1.  在复制线程运行时，可能存在间隙和半应用事务。

2.  [**mysqld**](mysqld.html)关闭。clean 和 unclean 关闭都会中止正在进行的事务，并可能留下空白和半应用事务。

3.  [`杀`](kill.html)复制线程（使用单线程副本时的 SQL 线程，使用多线程副本时的协调器线程）。这会中止正在进行的事务，并可能留下空白和半应用事务。

4.  应用程序线程中的错误。这可能会留下空白。如果错误出现在混合事务中，则该事务是半应用的。使用多线程副本时，未收到错误的工作人员会完成其队列，因此可能需要一些时间来停止所有线程。

5.  [`停止复制`](stop-replica.html)使用多线程副本时。发出后[`停止复制`](stop-replica.html)，副本等待任何间隙被填补，然后更新`Exec_master_log_pos`.这确保了它永远不会留下间隙或源二进制日志位置滞后，除非上述任何情况适用，换句话说，之前[`停止复制`](stop-replica.html)完成，发生错误或另一个线程问题[`杀`](kill.html)，或服务器重新启动。在这些情况下，[`停止复制`](stop-replica.html)成功返回。

6.  如果中继日志中的最后一个事务只收到了一半，并且多线程副本的协调线程已经开始将事务调度给一个worker，那么[`停止复制`](stop-replica.html)等待最多 60 秒以接收交易。在此超时之后，协调器放弃并中止事务。如果交易是混合的，它可能会半途而废。

7.  [`停止复制`](stop-replica.html)使用单线程副本时。如果正在进行的事务只更新事务表，则回滚并[`停止复制`](stop-replica.html)立即停止。如果正在进行的交易是混合的，[`停止复制`](stop-replica.html)等待交易完成最多 60 秒。在此超时之后，它会中止事务，因此它可能会半途而废。

    系统变量的全局设置[`rpl_stop_replica_timeout`](replication-options-replica.html#sysvar_rpl_stop_replica_timeout)（来自 MySQL 8.0.26）或[`rpl_stop_slave_timeout`](replication-options-replica.html#sysvar_rpl_stop_slave_timeout)（在 MySQL 8.0.26 之前）与停止复制线程的过程无关。它只会让客户发出问题[`停止复制`](stop-replica.html)返回客户端，但复制线程继续尝试停止。

    如果复制通道存在间隙，则会产生以下后果：

8.  副本数据库处于源上可能从未存在过的状态。

9.  场`Exec_master_log_pos`在[`显示副本状态`](show-replica-status.html)只是一个“低水位线”。换句话说，出现在该位置之前的事务保证已提交，但该位置之后的事务可能已提交或未提交。

10. [`将复制源更改为`](change-replication-source-to.html)和[`将主更改为`](change-master-to.html)该通道的语句失败并出现错误，除非应用程序线程正在运行并且该语句仅设置接收器选项。

11. 如果[**mysqld**](mysqld.html)开始于[`--中继日志恢复`](replication-options-replica.html#sysvar_relay_log_recovery)，不会对该通道进行恢复，并打印一条警告。

12. 如果[**mysql转储**](mysqldump.html)与[`--转储副本`](mysqldump.html#option_mysqldump_dump-replica)要么[`--dump-slave`](mysqldump.html#option_mysqldump_dump-slave)，它不记录间隙的存在；因此它打印[`将复制源更改为`](change-replication-source-to.html)\|[`将主更改为`](change-master-to.html)和`RELAY_LOG_POS`设置为“低水位线”位置`Exec_master_log_pos`.

    在另一台服务器上应用转储并启动复制线程后，再次复制该位置之后出现的事务。请注意，如果启用 GTID，这是无害的（但是，在这种情况下，不建议使用[`--转储副本`](mysqldump.html#option_mysqldump_dump-replica)要么[`--dump-slave`](mysqldump.html#option_mysqldump_dump-slave)）。

    如果复制通道有源二进制日志位置滞后但没有间隙，则适用上述情况 2 到 5，但情况 1 不适用。

    源二进制日志位置信息以二进制格式保存在内部表中`mysql.slave_worker_info`.[`启动副本 [SQL_THREAD]`](start-replica.html)始终查阅此信息，以便仅应用正确的交易。这仍然是正确的，即使[`replica_parallel_workers`](replication-options-replica.html#sysvar_replica_parallel_workers)要么[`slave_parallel_workers`](replication-options-replica.html#sysvar_slave_parallel_workers)之前已经改成0[`开始复制`](start-replica.html), 并且即使[`开始复制`](start-replica.html)与`直到`条款。[`开始复制直到 SQL_AFTER_MTS_GAPS`](start-replica.html)仅根据需要应用尽可能多的事务以填补空白。如果[`开始复制`](start-replica.html)与`直到`告诉它在消耗完所有间隙之前停止的子句，然后留下剩余的间隙。

警告

[`重置副本`](reset-replica.html)删除中继日志并重置复制位置。从而发出[`重置副本`](reset-replica.html)在具有间隙的多线程副本上意味着副本丢失有关间隙的任何信息，而不纠正间隙。在这种情况下，如果正在使用基于二进制日志位置的复制，则恢复过程将失败。

当使用基于 GTID 的复制时 ([`GTID_MODE=ON`](replication-options-gtids.html#sysvar_gtid_mode)） 和`SOURCE_AUTO_POSITION`使用[`将复制源更改为`](change-replication-source-to.html)声明，恢复过程不需要旧的中继日志。相反，副本可以使用 GTID 自动定位来计算与源相比它缺少哪些事务。从 MySQL 8.0.26 开始，当使用基于 GTID 的复制时，用于解决多线程副本上的间隙的基于二进制日志位置的复制的过程将被完全跳过。当该过程被跳过时，[`开始复制直到 SQL_AFTER_MTS_GAPS`](start-replica.html)语句的行为不同，并且不会尝试检查事务序列中的间隙。您也可以发出[`将复制源更改为`](change-replication-source-to.html)语句，在存在间隙的非 GTID 副本上是不允许的。
