GTID 的生命周期包括以下步骤:
- 事务在源上执行并提交。此客户端事务分配有一个 GTID,该 GTID 由源的 UUID 和此服务器上尚未使用的最小非零事务序列号组成。GTID 被写入源的二进制日志(紧接在日志中的事务本身之前)。如果客户端事务没有写入二进制日志(例如,因为事务被过滤掉,或者事务是只读的),则不会为其分配 GTID。
- 如果为事务分配了 GTID,则 GTID 在提交时通过在事务开始时将其写入二进制日志(以
Gtid_log_event. 每当二进制日志轮换或服务器关闭时,服务器都会将写入先前二进制日志文件的所有事务的 GTID 写入mysql.gtid_executed表中。 - 如果为事务分配了 GTID,则通过将 GTID 添加到
gtid_executed系统变量@@GLOBAL.gtid_executed( 此 GTID 集包含所有已提交 GTID 事务集的表示,并在复制中用作表示服务器状态的令牌。启用二进制日志记录(根据源的要求),gtid_executed系统变量中的 GTID 集是应用事务的完整记录,但mysql.gtid_executed表不是,因为最近的历史记录仍在当前二进制日志文件中。 - 在将二进制日志数据传输到副本并存储在副本的中继日志中(使用该过程的既定机制,详见 第 17.2 节,“复制实现”),副本读取 GTID 并设置其
gtid_next系统的值变量作为此 GTID。这告诉副本必须使用此 GTID 记录下一个事务。重要的是要注意副本集gtid_next在会话上下文中。 - 副本验证没有线程尚未获得 GTID 的所有权
gtid_next以处理事务。通过首先读取和检查复制事务的 GTID,在处理事务本身之前,副本不仅保证没有具有此 GTID 的先前事务已应用于副本,而且还没有其他会话已读取此 GTID 但尚未提交了关联的事务。因此,如果多个客户端尝试同时应用同一个事务,服务器会通过只让其中一个执行来解决此问题。系统gtid_owned变量 (@@GLOBAL.gtid_owned) 的副本显示当前正在使用的每个 GTID 以及拥有它的线程的 ID。如果 GTID 已被使用,则不会引发错误,并使用自动跳过功能忽略事务。 - 如果尚未使用 GTID,则副本应用复制的事务。因为
gtid_next设置为源已经分配的 GTID,所以副本不会尝试为此事务生成新的 GTID,而是使用存储在gtid_next. - 如果在副本上启用了二进制日志记录,则 GTID 在提交时通过在事务开始时将其写入二进制日志(以
Gtid_log_event. 每当二进制日志轮换或服务器关闭时,服务器都会将写入先前二进制日志文件的所有事务的 GTID 写入mysql.gtid_executed表中。 - 如果在副本上禁用了二进制日志记录,则 GTID 通过将其直接写入
mysql.gtid_executed表中以原子方式持久化。MySQL 将一条语句附加到事务以将 GTID 插入表中。从 MySQL 8.0 开始,这个操作对于 DDL 语句和 DML 语句都是原子的。在这种情况下,该mysql.gtid_executed表是副本上应用的事务的完整记录。 gtid_executed在副本上提交复制事务后不久,通过将 GTID 添加到副本的系统变量 (@@GLOBAL.gtid_executed) 中 的 GTID 集合中,GTID 被非原子地外部化 。至于源,这个 GTID 集包含所有已提交 GTID 事务集的表示。如果在副本上禁用二进制日志记录,则该mysql.gtid_executed表也是副本上应用的事务的完整记录。如果在副本上启用了二进制日志记录,这意味着某些 GTID 仅记录在二进制日志中,则gtid_executed系统变量中的 GTID 集是唯一完整的记录。
在源上完全过滤掉的客户端事务没有分配 GTID,因此它们不会添加到 gtid_executed系统变量中的事务集或添加到mysql.gtid_executed表中。但是,在副本上完全过滤掉的复制事务的 GTID 会保留下来。如果在副本上启用了二进制日志记录,则过滤出的事务将作为 a 写入二进制日志,然后是一个仅包含和 语句Gtid_log_event的空事务。如果禁用二进制日志记录,则将过滤出的事务的 GTID 写入表中。为过滤掉的事务保留 GTID 可确保 BEGIN``COMMIT``mysql.gtid_executed``mysql.gtid_executed表和gtid_executed系统变量中的 GTID 集可以被压缩。它还确保如果副本重新连接到源,则不会再次检索过滤掉的事务,如 第 17.1.3.3 节,“GTID 自动定位”中所述。
在多线程副本(使用 replica_parallel_workers > 0or slave_parallel_workers > 0)上,可以并行应用事务,因此复制的事务可以无序提交(除非 设置了replica_preserve_commit_order=1 or slave_preserve_commit_order=1 )。发生这种情况时, gtid_executed系统变量中的 GTID 集包含多个 GTID 范围,它们之间存在间隙。(在源或单线程副本上,GTID 单调增加,数字之间没有间隙。)多线程副本上的间隙仅出现在最近应用的事务中,并随着复制的进行而填充。当复制线程完全停止使用 STOP REPLICA语句,应用正在进行的事务以填补空白。如果发生关闭(例如服务器故障或使用该 KILL语句停止复制线程),则可能会保留空白。
哪些更改被分配了 GTID?
典型的场景是服务器为提交的事务生成一个新的 GTID。但是,GTID 也可以分配给除事务之外的其他更改,并且在某些情况下,可以为单个事务分配多个 GTID。
写入二进制日志的每个数据库更改(DDL 或 DML)都分配有一个 GTID。这包括自动提交的更改,以及使用 BEGINand COMMITor START TRANSACTION语句提交的更改。GTID 还分配给数据库的创建、更改或删除,以及非表数据库对象(例如过程、函数、触发器、事件、视图、用户、角色或授权)的创建、更改或删除。
为非事务性更新和事务性更新分配了 GTID。此外,对于非事务性更新,如果在尝试写入二进制日志缓存时发生磁盘写入失败并因此在二进制日志中创建间隙,则会为生成的事件日志事件分配一个 GTID。
当二进制日志中生成的语句自动删除表时,会为该语句分配一个 GTID。当副本开始应用来自刚刚启动的源的事件时,以及在使用基于语句的复制 ( binlog_format=STATEMENT) 并且具有打开的临时表的用户会话断开连接时,临时表会自动删除。使用MEMORY存储引擎的表在服务器启动后第一次访问时会被自动删除,因为在关闭期间行可能已经丢失。
当事务未写入源服务器上的二进制日志时,服务器不会为其分配 GTID。这包括回滚的事务和在源服务器上禁用二进制日志记录时执行的事务,无论是全局(--skip-log-bin 在服务器的配置中指定)还是会话(SET @@SESSION.sql_log_bin = 0)。这还包括使用基于行的复制时的无操作事务 ( binlog_format=ROW)。
XA 事务为事务的XA PREPARE阶段和事务的XA COMMITorXA ROLLBACK阶段分配了单独的 GTID。XA 事务是持久准备的,以便用户可以提交它们或在发生故障时回滚它们(在复制拓扑中可能包括故障转移到另一台服务器)。因此,事务的两个部分是分开复制的,因此它们必须有自己的 GTID,即使回滚的非 XA 事务没有 GTID。
在以下特殊情况下,单个语句可以生成多个事务,因此被分配多个 GTID:
- 调用提交多个事务的存储过程。为该过程提交的每个事务生成一个 GTID。
- 多表
DROP TABLE语句删除不同类型的表。如果任何表使用不支持原子 DDL 的存储引擎,或者如果任何表是临时表,则可以生成多个 GTID。 CREATE TABLE ... SELECT使用基于行的复制时会发出 一条 语句 (binlog_format=ROW)。为CREATE TABLE操作生成一个 GTID,为行插入操作生成一个 GTID。
系统gtid_next变量
默认情况下,对于用户会话中提交的新事务,服务器会自动生成并分配一个新的 GTID。当事务应用于副本时,来自源服务器的 GTID 将被保留。gtid_next 您可以通过设置系统变量 的会话值来更改此行为:
- 当
gtid_next设置为 时AUTOMATIC,这是默认值,并且事务被提交并写入二进制日志,服务器会自动生成并分配一个新的 GTID。如果事务回滚或由于其他原因未写入二进制日志,则服务器不会生成和分配 GTID。 - 如果您设置
gtid_next为有效的 GTID(由 UUID 和事务序列号组成,以冒号分隔),则服务器将该 GTID 分配给您的事务。gtid_executed即使事务未写入二进制日志或事务为空,也会 分配并添加此 GTID 。
请注意,在您设置 gtid_next为特定 GTID 并且事务已提交或回滚后,SET @@SESSION.gtid_next必须在任何其他语句之前发出显式语句。AUTOMATIC如果您不想显式分配更多 GTID,可以使用它来将 GTID 值设置回。
当复制应用程序线程应用复制事务时,它们使用此技术, @@SESSION.gtid_next明确设置为在源服务器上分配的复制事务的 GTID。这意味着保留来自原始服务器的 GTID,而不是由副本生成和分配新的 GTID。gtid_executed这也意味着即使在副本上禁用二进制日志记录或副本更新日志记录,或者当事务是无操作或在副本上被过滤掉时, GTID 也会添加到 副本上。
客户端可以通过@@SESSION.gtid_next在执行事务之前设置特定的 GTID 来模拟复制的事务。mysqlbinlog使用此技术 生成二进制日志的转储,客户端可以重播以保留 GTID。通过客户端提交的模拟复制事务完全等同于通过复制应用程序线程提交的复制事务,事后无法区分。
系统gtid_purged变量
gtid_purged系统变量 ( ) 中的 GTID 集 @@GLOBAL.gtid_purged包含服务器上已提交但不存在于服务器上的任何二进制日志文件中的所有事务的 GTID。 gtid_purged是 的子集 gtid_executed。以下类别的 GTID 位于 gtid_purged:
- 在副本上禁用二进制日志记录的情况下提交的复制事务的 GTID。
- 写入二进制日志文件的事务的 GTID,现在已被清除。
- 由语句显式添加到集合中的 GTID
SET @@GLOBAL.gtid_purged。
您可以更改 的值, gtid_purged以便在服务器上记录已应用某个 GTID 集中的事务,尽管它们不存在于服务器上的任何二进制日志中。当您将 GTID 添加到 时 gtid_purged,它们也会添加到gtid_executed. 此操作的一个示例用例是当您在服务器上恢复一个或多个数据库的备份时,但您没有包含服务器上事务的相关二进制日志。在 MySQL 8.0 之前,您只能更改 gtid_purgedwhen 的值gtid_executed(因此 gtid_purged) 是空的。从 MySQL 8.0 开始,此限制不再适用,您还可以选择是 gtid_purged用指定的 GTID 集替换整个 GTID 集,还是将指定的 GTID 集添加到 gtid_purged. 有关如何执行此操作的详细信息,请参阅 的说明 gtid_purged。
gtid_executed和 系统变量中 的 GTID 集在 gtid_purged服务器启动时初始化。每个二进制日志文件都以 event 开头 Previous_gtids_log_event,其中包含所有先前二进制日志文件中的 GTID 集(由先前文件中的 Previous_gtids_log_eventGTID 和先前文件本身中的每个 GTID 组成Gtid_log_event)。Previous_gtids_log_event最旧和最近的二进制日志文件中的 内容 用于计算服务器启动时 的gtid_executed和 集:gtid_purged
gtid_executed计算为Previous_gtids_log_event最近二进制日志文件中的 GTID、该二进制日志文件中事务的 GTID 以及存储在mysql.gtid_executed表中的 GTID 的并集。此 GTID 集包含服务器上已使用(或显式添加到gtid_purged)的所有 GTID,无论它们当前是否位于服务器上的二进制日志文件中。它不包括服务器上当前正在处理的事务的 GTID (@@GLOBAL.gtid_owned)。gtid_purged通过首先添加Previous_gtids_log_event最新二进制日志文件中的 GTID 和该二进制日志文件中事务的 GTID 来计算。此步骤提供当前或曾经记录在服务器 (gtids_in_binlog) 上的二进制日志中的 GTID 集。接下来,从Previous_gtids_log_event最旧的二进制日志文件中减去 GTIDgtids_in_binlog。这一步给出了当前记录在服务器二进制日志中的一组 GTID (gtids_in_binlog_not_purged)。最后,gtids_in_binlog_not_purged减去gtid_executed. 结果是已经在服务器上使用过但当前没有记录在服务器上的二进制日志文件中的 GTID 集合,这个结果用于初始化gtid_purged.
如果这些计算涉及 MySQL 5.7.7 或更早版本的二进制日志,则可能会为gtid_executed和 计算不正确的 GTID 集gtid_purged,即使稍后重新启动服务器,它们仍然不正确。有关详细信息,请参阅 binlog_gtid_simple_recovery 系统变量的描述,该变量控制如何迭代二进制日志以计算 GTID 集。如果其中描述的情况之一适用于服务器,请设置 binlog_gtid_simple_recovery=FALSE 在启动它之前在服务器的配置文件中。该设置使服务器迭代所有二进制日志文件(不仅仅是最新和最旧的)以查找 GTID 事件开始出现的位置。如果服务器有大量没有 GTID 事件的二进制日志文件,此过程可能需要很长时间。
重置 GTID 执行历史
如果您需要在服务器上重置 GTID 执行历史记录,请使用该RESET MASTER语句。例如,您可能需要在执行测试查询以验证新启用 GTID 的服务器上的复制设置后执行此操作,或者当您想要将新服务器加入复制组但它包含一些不被接受的不需要的本地事务时通过组复制。
警告
谨慎使用RESET MASTER以避免丢失任何想要的 GTID 执行历史记录和二进制日志文件。
在发布之前RESET MASTER,请确保您已经备份了服务器的二进制日志文件和二进制日志索引文件(如果有),并获取并保存保存在 gtid_executed系统变量的全局值中的 GTID 集(例如,通过发出SELECT @@GLOBAL.gtid_executed语句并保存结果)。如果您要从该 GTID 集中删除不需要的事务,请使用mysqlbinlog检查事务的内容以确保它们没有价值,不包含必须保存或复制的数据,并且不会导致服务器上的数据更改。
当您发出RESET MASTER时,将执行以下重置操作:
- 系统变量的值
gtid_purged设置为空字符串 ('')。 - 系统变量的全局值(但不是会话值)
gtid_executed设置为空字符串。 - 该
mysql.gtid_executed表被清除(参见 mysql.gtid_executed Table)。 - 如果服务器启用了二进制日志记录,则会删除现有的二进制日志文件并清除二进制日志索引文件。
请注意,RESET MASTER即使服务器是禁用二进制日志记录的副本,也可以重置 GTID 执行历史记录。 RESET REPLICA对 GTID 执行历史没有影响。




