#### 17.1.3.2 GTID生命周期

[](<>)

GTID的生命周期包括以下步骤：

1.  事务在源上执行和提交。此客户端事务被分配一个GTID，该GTID由源的UUID和此服务器上尚未使用的最小非零事务序列号组成。GTID被写入源的二进制日志（紧接着日志中的事务本身之前）。如果客户机事务没有写入二进制日志（例如，因为该事务被过滤掉，或者该事务是只读的），则不会为其分配GTID。

2.  如果为事务分配了GTID，则在提交时通过在事务开始时将GTID写入二进制日志（作为`Gtid_日志_事件`)。每当旋转二进制日志或关闭服务器时，服务器都会将写入前一个二进制日志文件的所有事务的GTID写入`mysql。gtid_已执行`桌子

3.  如果为事务分配了GTID，则通过将GTID添加到[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量(`@@全球。gtid_已执行`)。此GTID集包含所有提交的GTID事务集的表示，并在复制中用作表示服务器状态的令牌。在启用二进制日志记录（根据源代码的要求）的情况下[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量是应用的事务的完整记录，但`mysql。gtid_已执行`表不是，因为最近的历史仍在当前二进制日志文件中。

4.  二进制日志数据传输到副本并存储在副本的中继日志中后（使用此过程的既定机制，请参阅[第17.2节，“复制实施”](replication-implementation.html)，以获取详细信息），复制副本读取GTID并设置其值[`下一个`](replication-options-gtids.html#sysvar_gtid_next)系统变量作为这个GTID。这会告诉复制副本必须使用此GTID记录下一个事务。需要注意的是，复制副本集`下一个`在会话上下文中。

5.  复制副本验证中没有线程拥有GTID[`下一个`](replication-options-gtids.html#sysvar_gtid_next)以处理交易。通过先读取并检查复制事务的GTID，然后再处理事务本身，副本不仅可以保证以前没有在副本上应用过具有此GTID的事务，还可以保证没有其他会话已经读取了此GTID，但尚未提交关联的事务。因此，如果多个客户端试图同时应用同一事务，服务器将通过只让其中一个执行来解决这个问题。这个[`gtid_所有`](replication-options-gtids.html#sysvar_gtid_owned)系统变量(`@@全球。gtid_所有`)对于，复制副本显示当前正在使用的每个GTID以及拥有它的线程的ID。如果已经使用了GTID，则不会引发错误，并使用自动跳过功能来忽略事务。

6.  如果未使用GTID，复制副本将应用复制的事务。因为[`下一个`](replication-options-gtids.html#sysvar_gtid_next)设置为源已分配的GTID时，复制副本不会尝试为此事务生成新的GTID，而是使用存储在中的GTID[`下一个`](replication-options-gtids.html#sysvar_gtid_next).

7.  如果在复制副本上启用了二进制日志记录，则GTID将在事务开始时写入二进制日志（作为`Gtid_日志_事件`)。每当旋转二进制日志或关闭服务器时，服务器都会将写入前一个二进制日志文件的所有事务的GTID写入`mysql。gtid_已执行`桌子

8.  如果在复制副本上禁用二进制日志记录，则通过将GTID直接写入`mysql。gtid_已执行`桌子MySQL向事务追加一条语句，将GTID插入表中。从MySQL 8.0开始，这个操作对于DDL语句和DML语句都是原子的。在这种情况下`mysql。gtid_已执行`表是应用于复制副本的事务的完整记录。

9.  在副本上提交复制事务后不久，通过将GTID添加到[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量(`@@全球。gtid_已执行`)为了复制品。至于源，这个GTID集包含所有提交的GTID事务集的表示。如果在复制副本上禁用了二进制日志记录，则`mysql。gtid_已执行`表也是应用于复制副本的事务的完整记录。如果在副本上启用了二进制日志记录，这意味着某些GTID仅记录在二进制日志中，则[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量是唯一完整的记录。

    在源上完全过滤掉的客户端事务没有分配GTID，因此它们不会添加到源中的事务集合中[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量，或添加到`mysql。gtid_已执行`桌子但是，在复制副本上完全过滤掉的复制事务的GTID将被持久化。如果在复制副本上启用了二进制日志记录，则过滤掉的事务将作为`Gtid_日志_事件`后面是一个空事务，只包含`开始`和`犯罪`声明。如果禁用二进制日志记录，则过滤掉的事务的GTID将写入`mysql。gtid_已执行`桌子为过滤掉的事务保留GTID可确保`mysql。gtid_已执行`表和中的GTID集[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量可以被压缩。它还可以确保，如果复制副本重新连接到源，则不会再次检索筛选出的事务，如中所述[第17.1.3.3节，“GTID自动定位”](replication-gtids-auto-positioning.html).

    在多线程复制副本上（使用[`副本\u并行\u工作者>0`](replication-options-replica.html#sysvar_replica_parallel_workers)或[`从\u并行\u工作者>0`](replication-options-replica.html#sysvar_slave_parallel_workers))，事务可以并行应用，因此复制的事务可以无序提交（除非[`副本保存提交顺序=1`](replication-options-replica.html#sysvar_replica_preserve_commit_order)或[`从属\u保存\u提交\u顺序=1`](replication-options-replica.html#sysvar_slave_preserve_commit_order)已设置）。当这种情况发生时[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量包含多个GTID范围，它们之间有间隙。（在源或单线程复制副本上，GTID单调递增，数字之间没有间隙。）多线程副本上的漏洞仅出现在最近应用的事务中，并随着复制的进行而填补。当使用[`停止复制`](stop-replica.html)声明中，应用正在进行的事务，以便填补空白。在服务器故障或使用[`杀死`](kill.html)语句停止复制线程，间隙可能仍然存在。

##### 哪些更改被分配了GTID？

典型的情况是，服务器为提交的事务生成一个新的GTID。但是，GTID也可以分配给事务之外的其他更改，在某些情况下，一个事务可以分配多个GTID。

写入二进制日志的每个数据库更改（DDL或DML）都被分配一个GTID。这包括自动提交的更改，以及使用`开始`和`犯罪`或`开始交易`声明。GTID还被分配给数据库和非表数据库对象（如过程、函数、触发器、事件、视图、用户、角色或授权）的创建、更改或删除。

为非事务性更新和事务性更新分配GTID。此外，对于非事务性更新，如果在尝试写入二进制日志缓存时发生磁盘写入故障，并且因此在二进制日志中创建了间隙，则会为生成的事件日志事件分配一个GTID。

当一个表被二进制日志中生成的语句自动删除时，一个GTID被分配给该语句。当复制副本开始应用来自刚启动的源的事件时，以及在使用基于语句的复制时，会自动删除临时表([`binlog_format=语句`](replication-options-binary-log.html#sysvar_binlog_format))而一个具有打开的临时表的用户会话将断开连接。使用[`记忆力`](memory-storage-engine.html)存储引擎在服务器启动后首次访问时会自动删除，因为行可能在关机期间丢失。

当事务未写入源服务器上的二进制日志时，服务器不会为其分配GTID。这包括回滚的事务，以及在源服务器上全局禁用二进制日志记录时执行的事务（使用`--跳过日志箱`在服务器的配置中指定）或用于会话(`设置@会话。sql_log_bin=0`)。这还包括在使用基于行的复制时没有op事务([`binlog_格式=行`](replication-options-binary-log.html#sysvar_binlog_format)).

XA事务为`XA准备`交易的阶段和`XA提交`或`XA回滚`交易的阶段。XA事务是持续准备的，因此用户可以在出现故障时提交它们或回滚它们（在复制拓扑中可能包括到另一台服务器的故障切换）。因此，事务的两个部分是分开复制的，因此它们必须有自己的GTID，即使回滚的非XA事务不会有GTID。

在以下特殊情况下，一条语句可以生成多个事务，因此可以分配多个GTID：

-   调用一个提交多个事务的存储过程。为过程提交的每个事务生成一个GTID。

-   多张桌子[`升降台`](drop-table.html)语句会删除不同类型的表。如果任何表使用不支持原子DDL的存储引擎，或者任何表是临时表，则可以生成多个GTID。

-   A.[`创建表格。。。选择`](create-table-select.html)语句在使用基于行的复制时发出([`binlog_格式=行`](replication-options-binary-log.html#sysvar_binlog_format)).生成一个GTID用于[`创建表格`](create-table.html)操作，并为行插入操作生成一个GTID。

##### 这个`下一个`系统变量

默认情况下，对于用户会话中提交的新事务，服务器会自动生成并分配新的GTID。当事务应用于副本时，源服务器的GTID将被保留。您可以通过设置[`下一个`](replication-options-gtids.html#sysvar_gtid_next)系统变量：

-   什么时候[`下一个`](replication-options-gtids.html#sysvar_gtid_next)即将`自动的`，这是默认设置，当事务提交并写入二进制日志时，服务器会自动生成并分配新的GTID。如果事务回滚或由于其他原因未写入二进制日志，服务器不会生成并分配GTID。

-   如果你设定[`下一个`](replication-options-gtids.html#sysvar_gtid_next)对于有效的GTID（由UUID和事务序列号组成，用冒号分隔），服务器将该GTID分配给您的事务。此GTID被分配并添加到[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)即使事务未写入二进制日志，或者事务为空。

    请注意，设置后[`下一个`](replication-options-gtids.html#sysvar_gtid_next)对于特定的GTID，并且事务已提交或回滚，显式`设置@会话。下一个`声明必须在任何其他声明之前发布。您可以使用它将GTID值设置回`自动的`如果您不想显式分配更多GTID。

    当复制应用程序线程应用复制的事务时，它们使用以下技术：设置`@@会议。下一个`显式地分配给源服务器上已复制事务的GTID。这意味着保留来自源服务器的GTID，而不是由复制副本生成和分配新的GTID。这也意味着GTID被添加到[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)即使在副本上禁用了二进制日志记录或副本更新日志记录，或者在副本上事务为无操作或被过滤掉时，也会在副本上进行。

    客户端可以通过设置`@@会议。下一个`在执行事务之前，将其复制到特定的GTID。这项技术由[**mysqlbinlog**](mysqlbinlog.html)生成二进制日志转储，客户端可以重播该转储以保留GTID。通过客户端提交的模拟复制事务完全等同于通过复制应用程序线程提交的复制事务，并且在事后无法区分它们。

##### 这个`gtid_`系统变量

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

中的GTID集[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)系统变量(`@@全球。gtid_`)包含服务器上已提交的所有事务的GTID，但不存在于服务器上的任何二进制日志文件中。[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)是[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)。以下类别的GTID为[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged):

-   在副本上禁用二进制日志记录时提交的已复制事务的GTID。

-   写入二进制日志文件（现已清除）的事务的GTID。

-   由语句显式添加到集合中的GTID`设置@@GLOBAL。gtid_`.

    您可以更改[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)以便在服务器上记录已应用某个GTID集中的事务，尽管它们不存在于服务器上的任何二进制日志中。当您将GTID添加到[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)，它们也被添加到[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)。此操作的一个示例用例是，当您正在恢复服务器上一个或多个数据库的备份，但服务器上没有包含事务的相关二进制日志时。在MySQL 8.0之前，您只能更改[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)什么时候[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)（因此[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged))空无一人。在MySQL 8.0中，这个限制不适用，您还可以选择是否替换中设置的整个GTID[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)使用指定的GTID集，或将指定的GTID集添加到已存在的GTID中[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)。有关如何执行此操作的详细信息，请参阅的说明[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged).

    中的GTID集[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)和[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)系统变量在服务器启动时初始化。每个二进制日志文件都以事件开头`上一次_gtids_log_事件`，它包含所有以前的二进制日志文件（由前一个文件的`上一次_gtids_log_事件`，以及每个`Gtid_日志_事件`在前面的文件中）。内容`上一次_gtids_log_事件`在最古老和最新的二进制日志文件中，用于计算[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)和[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)在服务器启动时设置：

-   [`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)计算为中GTID的并集`上一次_gtids_log_事件`在最新的二进制日志文件中，该二进制日志文件中事务的GTID，以及存储在`mysql。gtid_已执行`桌子此GTID集包含已使用（或明确添加到）的所有GTID[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged))在服务器上，无论它们当前是否位于服务器上的二进制日志文件中。它不包括当前正在服务器上处理的事务的GTID(`@@全球。gtid_所有`).

-   [`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)首先将GTID添加到`上一次_gtids_log_事件`在最新的二进制日志文件中，以及该二进制日志文件中事务的GTID。这一步给出了当前或曾经记录在服务器二进制日志中的GTID集(`gtids_in_binlog`).下一步是GTID`上一次_gtids_log_事件`在最旧的二进制日志文件中，从`gtids_in_binlog`。此步骤给出当前记录在服务器二进制日志中的GTID集(`GTID_in_binlog_未被清除`).最后，`GTID_in_binlog_未被清除`从中减去[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)。结果是服务器上已使用但当前未记录在服务器二进制日志文件中的GTID集，此结果用于初始化[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged).

    如果这些计算涉及MySQL 5.7.7或更早版本的二进制日志，则可能会计算不正确的GTID集[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)和[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)，即使服务器稍后重新启动，它们仍然不正确。有关详细信息，请参阅[`binlog_gtid_简单_恢复`](replication-options-gtids.html#sysvar_binlog_gtid_simple_recovery)系统变量，它控制如何迭代二进制日志以计算GTID集。如果其中一种情况适用于服务器，请设置[`binlog_gtid_simple_recovery=FALSE`](replication-options-gtids.html#sysvar_binlog_gtid_simple_recovery)在服务器的配置文件中，然后再启动它。该设置使服务器迭代所有二进制日志文件（不仅仅是最新和最旧的）以查找GTID事件开始出现的位置。如果服务器有大量没有GTID事件的二进制日志文件，则此过程可能需要很长时间。

##### 重置GTID执行历史记录

如果需要重置服务器上的GTID执行历史记录，请使用[`重置主机`](reset-master.html)陈述例如，在执行测试查询以验证启用GTID的新服务器上的复制设置之后，或者当您希望将新服务器加入到复制组，但其中包含一些不受组复制接受的不需要的本地事务时，可能需要执行此操作。

警告

使用[`重置主机`](reset-master.html)小心避免丢失任何想要的GTID执行历史记录和二进制日志文件。

发行前[`重置主机`](reset-master.html)，确保备份了服务器的二进制日志文件和二进制日志索引文件（如果有），并获取并保存保存在[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量（例如，通过发出`选择@全局。gtid_已执行`语句并保存结果）。如果要从该GTID集中删除不需要的事务，请使用[**mysqlbinlog**](mysqlbinlog.html)检查事务的内容，以确保它们没有价值，不包含必须保存或复制的数据，并且不会导致服务器上的数据更改。

当你发布[`重置主机`](reset-master.html)，执行以下重置操作：

-   价值[`gtid_`](replication-options-gtids.html#sysvar_gtid_purged)系统变量设置为空字符串(`''`).

-   的全局值（但不是会话值）[`gtid_已执行`](replication-options-gtids.html#sysvar_gtid_executed)系统变量设置为空字符串。

-   这个`mysql。gtid_已执行`桌子已清空（参见[mysql。gtid\_执行表](replication-gtids-concepts.html#replication-gtids-gtid-executed-table)).

-   如果服务器启用了二进制日志记录，则会删除现有的二进制日志文件，并清除二进制日志索引文件。

    注意[`重置主机`](reset-master.html)是重置GTID执行历史记录的方法，即使服务器是禁用二进制日志记录的副本。[`重置副本`](reset-replica.html)对GTID执行历史没有影响。
