Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


19.1.3.2 GTID 生命周期

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

  1. 在源服务器上执行和提交事务。该客户端事务被分配一个GTID,组成源服务器的UUID和该服务器上尚未使用的最小非零事务序列号。GTID写入源服务器的二进制日志(在事务本身之前)。如果客户端事务未被写入二进制日志(例如,因为事务被过滤或事务是只读的),则不分配GTID。

  2. 如果分配了GTID,则在事务提交时atomically将GTID写入二进制日志(作为Gtid_log_event)。在二进制日志轮转或服务器关闭时,服务器将写入之前二进制日志文件中的所有事务的GTID到mysql.gtid_executed表中。

  3. 如果分配了GTID,则在事务提交后非atomically(非常短暂)通过将GTID添加到gtid_executed系统变量(@@GLOBAL.gtid_executed)中的GTID集中。这GTID集包含了所有已提交的GTID事务的表示形式,并且在复制中用于表示服务器状态。启用二进制日志记录(如源服务器所需)时,gtid_executed系统变量中的GTID集是对已应用的事务记录,但mysql.gtid_executed表不是,因为最新的历史记录仍然在当前二进制日志文件中。

  4. 在二进制日志数据传输到副本并存储在副本的relay log(使用已建立的机制进行该过程,见第19.2节,“Replication Implementation”,了解详细信息),副本读取GTID并将其设置为其gtid_next系统变量的值。这告诉副本下一个事务必须使用该GTID。请注意,副本在会话上下文中设置gtid_next

  5. 副本验证没有线程已经拥有GTID在gtid_next中,以处理事务。通过读取和检查复制事务的GTID,然后再处理事务本身,副本保证了不仅事务没有在副本上应用,还没有其他会话已经读取了该GTID但还没有提交关联事务。因此,如果多个客户端尝试并发地应用相同事务,服务器将解决这个问题,让其中一个执行。副本的gtid_owned系统变量(@@GLOBAL.gtid_owned)显示每个GTID当前在使用的ID和拥有该GTID的线程ID。如果GTID已经被使用,不会引发错误,而是使用自动跳过函数忽略事务。

  6. 如果GTID未被使用,备援服务器将应用复制的事务。因为gtid_next已被设置为源服务器分配的GTID,因此备援服务器不会尝试为该事务生成新的GTID,而是使用存储在gtid_next中的GTID。

  7. 如果备援服务器启用了二进制日志记录,GTID将在事务提交时原子地写入到二进制日志的开头(作为一个Gtid_log_event)。每当二进制日志文件被旋转或服务器关闭时,服务器将将之前二进制日志文件中的所有事务的GTID写入到mysql.gtid_executed表中。

  8. 如果备援服务器禁用了二进制日志记录,GTID将原子地写入到mysql.gtid_executed表中。MySQL将添加一个语句将GTID插入到表中。这一操作对于DDL语句和DML语句都是原子的。在这种情况下,mysql.gtid_executed表是备援服务器上应用的事务记录。

  9. 在备援服务器上提交事务后,GTID将在非原子地外部化,添加到gtid_executed系统变量(@@GLOBAL.gtid_executed)中的GTID集合中。对于源服务器,这个GTID集合包含了所有已提交的GTID事务。如果备援服务器禁用了二进制日志记录,mysql.gtid_executed表也是备援服务器上应用的事务记录。如果备援服务器启用了二进制日志记录,意味着一些GTID只记录在二进制日志中,gtid_executed系统变量中的GTID集合是唯一的记录。

客户端事务,如果在源服务器上完全被过滤掉,不会分配GTID,因此它们不会添加到gtid_executed系统变量中的事务集合中,也不会添加到mysql.gtid_executed表中。然而,备援服务器上完全被过滤掉的事务的GTID将被 persisted。如果备援服务器启用了二进制日志记录,过滤掉的事务将写入到二进制日志中作为一个Gtid_log_event,然后是包含只有BEGINCOMMIT语句的空事务。如果禁用了二进制日志记录,过滤掉的事务的GTID将写入到mysql.gtid_executed表中。保留过滤掉的事务的GTID确保了mysql.gtid_executed表和gtid_executed系统变量中的GTID集合可以被压缩。它也确保了过滤掉的事务在备援服务器重新连接到源服务器时不会被重新检索,如第19.1.3.3节,“GTID Auto-Positioning”中所述。

在多线程复制副本(具有replica_parallel_workers > 0)中,事务可以并行应用,因此复制事务可以在不保持提交顺序的情况下提交(除非replica_preserve_commit_order = 1)。当这种情况发生时,gtid_executed系统变量中的GTID范围可能包含间隙。 (在源或单线程副本中,GTIDs是单调递增的,没有间隙。)多线程副本中间隙只出现在最近应用的事务中,并且在复制进度中被填充。在使用STOP REPLICA语句停止复制线程时,正在进行的事务将被应用,以填充间隙。在服务器故障或使用KILL语句停止复制线程时,间隙可能保持不变。

典型情况是服务器为已提交事务生成新的GTID。然而,GTIDs也可以分配给其他变化,某些情况下一个事务可以分配多个GTIDs。

每个数据库变化(DDL或DML)都将被分配一个GTID。这包括自动提交的变化,以及使用BEGINCOMMITSTART TRANSACTION语句提交的变化。GTID也将被分配给数据库的创建、修改或删除,以及非表数据库对象的创建、修改或删除,如过程、函数、触发器、事件、视图、用户、角色或授权。

非事务更新也将被分配GTID。此外,对于非事务更新,如果在尝试将数据写入二进制日志缓存时出现磁盘写入故障,并且因此在二进制日志中创建了间隙,那么导致的事件日志事件将被分配GTID。

当表被自动删除时,GTID将被分配给语句。临时表在副本开始应用事件时自动被删除,当使用binlog_format=STATEMENT时,用户会话关闭时也会自动删除。使用MEMORY存储引擎的表将在服务器启动时首次访问时自动删除,因为可能在关闭时丢失了行。

当事务未被写入原始服务器的二进制日志时,服务器不会为其分配GTID。这包括回滚的事务和在原始服务器上禁用二进制日志功能(使用--skip-log-bin指定在服务器配置中)或会话(SET @@SESSION.sql_log_bin = 0)的事务。此外,还包括在使用行级别复制(binlog_format=ROW)时的空操作事务。

XA事务在事务的XA PREPAREXA COMMITXA ROLLBACK阶段分配单独的GTID。XA事务是持久准备的,以便在失败时可以提交或回滚(在复制拓扑结构中可能包括服务器故障)。因此,这两个事务部分需要单独的GTID,即使非XA事务回滚不需要GTID。

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

  • 存储过程被调用,以提交多个事务。为每个事务生成一个GTID。

  • 多表DROP TABLE语句删除不同类型的表。可以生成多个GTID,如果任何表使用不支持原子DDL的存储引擎,或者如果任何表是临时表。

  • 在使用行级别复制(binlog_format=ROW)时,CREATE TABLE ... SELECT语句被执行。生成一个GTID为CREATE TABLE操作和一个GTID为行插入操作。

默认情况下,对于用户会话中的新事务,服务器自动生成并分配新的GTID。事务在复制服务器上应用时,原始服务器的GTID将被保留。您可以通过设置gtid_next系统变量更改这个行为:

  • gtid_next设置为AUTOMATIC(默认)并且事务被提交并写入二进制日志时,服务器自动生成并分配新的GTID。如果事务回滚或未被写入二进制日志以其他原因,服务器不生成和分配GTID。

  • 如果您将gtid_next设置为AUTOMATIC:TAG,每个新事务将被分配包括指定标签的新的GTID。

  • 如果将gtid_next设置为有效的GTID(由UUID、可选标签和事务序列号组成,使用冒号分隔),服务器将将该GTID分配给您的事务。该GTID将被分配并添加到gtid_executed,即使事务未写入二进制日志,也即使事务为空。

请注意,在将gtid_next设置为特定的GTID(在UUID:NUMBERUUID:TAG:NUMBER格式)后,事务已经提交或回滚,您需要在执行任何其他语句之前执行明确的SET @@SESSION.gtid_next语句。您可以使用该语句将GTID值设置回AUTOMATIC,以便不再明确地分配GTID。

当复制应用程序线程应用复制事务时,它们将使用这种技术,设置@@SESSION.gtid_next以GTID为已分配的服务器的GTID。这意味着来自源服务器的GTID将被保留,而不是在副本上生成并分配新的GTID。它也意味着GTID将被添加到gtid_executed副本,即使在副本上禁用二进制日志记录或副本更新记录,也即使事务为空或在副本上被过滤。

客户端可以模拟复制事务,通过设置@@SESSION.gtid_next到特定的GTID,然后执行事务。这项技术由mysqlbinlog用于生成二进制日志的备份,该客户端可以重新播放该备份以保留GTID。模拟的复制事务提交通过客户端是与通过复制应用程序线程提交的复制事务相同的,且无法在事后区分。

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

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

  • 事务的GTID,已写入到现在已经被删除的二进制日志文件中。

  • 通过语句SET @@GLOBAL.gtid_purged明确地添加到gtid_purged中的GTID。

您可以将gtid_purged的值更改,以在服务器上记录某个GTID集中的事务已经被应用,但是这些事务在服务器上的任何二进制日志中都不存在。将GTIDs添加到gtid_purged中,也将其添加到gtid_executed中。该操作的示例用例是,您正在恢复一个或多个数据库的备份,但是您没有在服务器上拥有相关的二进制日志文件中的事务。您也可以选择将指定的GTID集替换gtid_purged中的整个GTID集,或者将指定的GTID集添加到gtid_purged中的GTIDs中。有关如何执行此操作的详细信息,请参阅gtid_purged的描述。

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

  • gtid_executed是计算最 recent二进制日志文件中的Previous_gtids_log_event中的GTID集、该二进制日志文件中的事务GTID和mysql.gtid_executed表中的GTID集。这GTID集包含了服务器上所有已经使用过的(或添加到gtid_purged)GTID集,是否当前在服务器上存在二进制日志文件中。它不包括当前正在被处理的服务器上的事务@@GLOBAL.gtid_owned

  • gtid_purged 是通过首先将 Previous_gtids_log_event 中的 GTID 在最新的二进制日志文件中添加,然后是该二进制日志文件中的事务 GTID。这个步骤给出了当前或曾经在服务器上记录在二进制日志中的 GTID 的集合 (gtids_in_binlog)。然后,Previous_gtids_log_event 中的 GTID 在最老的二进制日志文件中被从 gtids_in_binlog 中 subtract。这个步骤给出了当前在服务器上记录在二进制日志中的 GTID 的集合 (gtids_in_binlog_not_purged)。最后,gtids_in_binlog_not_purged 被从 gtid_executed 中 subtract。结果是服务器上已经使用过的 GTID,但当前不再记录在服务器上的二进制日志文件中的 GTID,这个结果用于初始化 gtid_purged

如果您需要在服务器上重置 GTID 执行历史记录,请使用 RESET BINARY LOGS AND GTIDS 语句。您可能需要在新 GTID 服务器上执行测试查询以验证复制设置,也可能需要在加入复制组的新服务器上,但该服务器包含一些不想被 Group Replication 接受的本地事务。

Warning

使用 RESET BINARY LOGS AND GTIDS 时请小心,以免丢失想要的 GTID 执行历史记录和二进制日志文件。

在执行 RESET BINARY LOGS AND GTIDS 之前,请确保您已经备份了服务器的二进制日志文件和二进制日志索引文件,如果有,请保存 gtid_executed 系统变量的全局值(例如,通过执行 SELECT @@GLOBAL.gtid_executed 语句并保存结果)。如果您要从该 GTID 集合中删除不想要的事务,请使用 mysqlbinlog 来检查事务内容,以确保它们没有价值、没有保存或复制的数据、没有在服务器上进行数据更改。

当您执行 RESET BINARY LOGS AND GTIDS 时,以下操作将被执行:

  • gtid_purged 系统变量的值设置为一个空字符串 ('').

  • gtid_executed 系统变量的全局值(但不是会话值)设置为一个空字符串。

  • 清除了 mysql.gtid_executed 表(见mysql.gtid_executed 表)。

  • 如果服务器启用了二进制日志记录,现有的二进制日志文件将被删除,二进制日志索引文件将被清除。

请注意,即使服务器是副本且二进制日志记录被禁用,也可以使用RESET BINARY LOGS AND GTIDS重置 GTID 执行历史。 RESET REPLICA 对 GTID 执行历史无效。