16.1.3.2 GTID 生命周期

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

未在源上完全过滤掉的 Client 端事务未分配 GTID,因此不会将它们添加到gtid_executed系统变量中的事务集中,也不会添加到mysql.gtid_executedtable 中。但是,在副本上完全过滤掉的复制事务的 GTID 会保留。如果在副本服务器上启用了二进制日志记录,则过滤出的事务将以Gtid_log_event的形式写入二进制日志,然后是仅包含BEGINCOMMIT语句的空事务。如果禁用了二进制日志记录,则将过滤出的事务的 GTID 写入mysql.gtid_executedtable。为过滤出的事务保留 GTID,可确保可以压缩mysql.gtid_executedtable 和gtid_executed系统变量中的 GTID 集。如第 16.1.3.3 节“ GTID 自动定位”所述,它还确保如果副本重新连接到源,则不会再次检索过滤出的事务。

在多线程副本(带有slave_parallel_workers> 0)上,可以并行应用事务,因此复制的事务可以无序提交(除非设置了slave_preserve_commit_order=1)。发生这种情况时,gtid_executed系统变量中的 GTID 集将包含多个 GTID 范围,它们之间存在间隙。 (在源或单线程副本上,GTID 将单调增加,而数字之间没有间隔.)多线程副本上的间隙仅出现在最近应用的事务中,并且随着复制的进行而被填充。当使用STOP SLAVE语句完全停止复制线程时,将应用进行中的事务以填补空白。在诸如服务器故障之类的关闭事件或使用KILL语句停止复制线程的情况下,间隙可能仍然存在。

为 GTID 分配了哪些更改?

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

写入二进制日志的每个数据库更改(DDL 或 DML)都分配有一个 GTID。这包括自动提交的更改以及使用BEGINCOMMITSTART TRANSACTION语句提交的更改。 GTID 还分配给数据库以及非 table 数据库对象(例如过程,函数,触发器,事件,视图,用户,角色或授权)的创建,更改或删除。

非事务更新以及事务更新都分配了 GTID。另外,对于非事务性更新,如果在尝试写入二进制日志缓存时发生磁盘写故障,因此在二进制日志中创建了一个间隙,则将所得的事件日志事件分配给 GTID。

当二进制日志中生成的语句自动删除 table 时,会将 GTID 分配给该语句。当副本开始从刚刚启动的源中应用事件时,以及在使用基于语句的复制(binlog_format=STATEMENT)并且具有打开的临时 table 的用户会话断开连接时,临时 table 将自动删除。在启动服务器后,首次访问使用MEMORY存储引擎的 table 会被自动删除,因为在关闭过程中可能会丢失行。

如果未将事务写入原始服务器上的二进制日志,则服务器不会为其分配 GTID。这包括回退的事务和在原始服务器上禁用二进制日志记录时执行的事务,这些服务器可以是全局(在服务器配置中指定--skip-log-bin)或会话(SET @@SESSION.sql_log_bin = 0)。当使用基于行的复制(binlog_format=ROW)时,这还包括无操作事务。

在 Transaction 的XA PREPARE阶段和 Transaction 的XA COMMITXA ROLLBACK阶段,为 XATransaction 分配了单独的 GTID。 XA 事务是持久准备的,以便用户可以在发生故障(在复制拓扑中可能包括故障转移到另一台服务器)的情况下提交或回滚它们。因此,事务的两个部分是分别复制的,因此,即使回滚的非 XA 事务不会具有 GTID,它们也必须具有自己的 GTID。

在以下特殊情况下,单个语句可以生成多个事务,因此可以分配多个 GTID:

gtid_next 系统变量

默认情况下,对于在用户会话中提交的新事务,服务器会自动生成并分配一个新的 GTID。将事务应用于副本时,将保留源服务器的 GTID。您可以通过设置gtid_next系统变量的会话值来更改此行为:

请注意,在将gtid_next设置为特定的 GTID 之后,并且事务已被提交或回滚,必须在任何其他语句之前发出显式的SET @@SESSION.gtid_next语句。如果您不想显式分配更多的 GTID,则可以使用它将 GTID 值设置回AUTOMATIC

当复制应用程序线程应用复制的事务时,它们使用此技术,将@@SESSION.gtid_next显式设置为原始服务器上分配的复制事务的 GTID。这意味着将保留源服务器的 GTID,而不是副本生成并分配新的 GTID。这也意味着即使在副本上禁用了二进制日志记录或副本更新日志,或者在无操作或已在副本上过滤掉事务的情况下,GTID 也会添加到副本上的gtid_executed

Client 端可以通过在执行事务之前将@@SESSION.gtid_next设置为特定的 GTID 来模拟复制的事务。 mysqlbinlog使用此技术来生成二进制日志的转储,Client 端可以重播该日志以保留 GTID。通过 Client 端提交的模拟复制事务与通过复制应用程序线程提交的复制事务完全等效,因此事后无法对其进行区分。

gtid_purged 系统变量

gtid_purged系统变量(@@GLOBAL.gtid_purged)中的 GTID 集合包含服务器上已提交但服务器上任何二进制日志文件中不存在的所有事务的 GTID。 gtid_purgedgtid_executed的子集。以下类别的 GTID 位于gtid_purged中:

您可以更改gtid_purged的值,以在服务器上记录已应用某些 GTID 集中的事务,尽管服务器上任何二进制日志中都不存在这些事务。将 GTID 添加到gtid_purged时,它们也将添加到gtid_executed。此操作的一个示例用例是,正在还原服务器上一个或多个数据库的备份,但是没有包含服务器上事务的相关二进制日志。在 MySQL 5.7 中,只能在gtid_executed(因此gtid_purged)为空时更改gtid_purged的值。有关如何执行此操作的详细信息,请参见gtid_purged的描述。

服务器启动时,将初始化gtid_executedgtid_purged系统变量中的 GTID 集。每个二进制日志文件均以事件Previous_gtids_log_event开头,该事件包含所有先前二进制日志文件中的 GTID 集(由先前文件Previous_gtids_log_event中的 GTID 和先前文件本身中每个Gtid_log_event的 GTID 组成)。最早和最新的二进制日志文件中Previous_gtids_log_event的内容用于在服务器启动时计算gtid_executedgtid_purged集:

如果这些计算涉及 MySQL 5.7.7 或更早版本的二进制日志,则可能会为gtid_executedgtid_purged计算不正确的 GTID 集,即使稍后重新启动服务器,它们也仍然不正确。有关详细信息,请参见binlog_gtid_simple_recovery系统变量的描述,该变量控制如何迭代二进制日志以计算 GTID 集。如果此处描述的情况之一适用于服务器,请在启动服务器之前在服务器的配置文件中设置binlog_gtid_simple_recovery=FALSE。该设置使服务器可以迭代所有二进制日志文件(而不仅仅是最新的和最旧的),以查找 GTID 事件开始出现的位置。如果服务器具有大量没有 GTID 事件的二进制日志文件,则此过程可能需要很长时间。

重置 GTID 执行历史

如果需要在服务器上重置 GTID 执行历史记录,请使用RESET MASTER语句。例如,在执行测试查询以验证新启用了 GTID 的服务器上的复制设置之后,或者当您要将新服务器加入复制组但其中包含一些不被接受的本地事务时,您可能需要执行此操作通过组复制。

Warning

请谨慎使用RESET MASTER,以避免丢失任何所需的 GTID 执行历史记录和二进制日志文件。

在发出RESET MASTER之前,请确保已备份服务器的二进制日志文件和二进制日志索引文件(如果有),并获取并保存保存在gtid_executed系统变量的全局值中的 GTID 集(例如,通过发出SELECT @@GLOBAL.gtid_executed语句)并保存结果)。如果要从该 GTID 集中删除不需要的事务,请使用mysqlbinlog检查事务的内容,以确保它们没有价值,不包含任何必须保存或复制的数据,并且不会导致服务器上的数据更改。

发出RESET MASTER时,将执行以下重置操作:

请注意,即使服务器是禁用了二进制日志记录的副本,RESET MASTER也是重置 GTID 执行历史记录的方法。 RESET SLAVE对 GTID 执行历史没有影响。

首页