GTID 的生命周期包括以下步骤:
在复制源服务器上执行并提交事务。此客户端事务被分配了一个 GTID,该 GTID 由源的 UUID 和尚未在此服务器上使用的最小非零事务序列号组成。GTID 被写入源的二进制日志(紧接在日志中的事务本身之前)。如果客户端事务未写入二进制日志(例如,因为事务被过滤掉,或者事务是只读的),则不会为其分配 GTID。
如果为事务分配了 GTID,则 GTID 在提交时通过在事务开始时将其写入二进制日志(作为
Gtid_log_event
)以原子方式持久化。每当二进制日志轮换或服务器关闭时,服务器都会将写入先前二进制日志文件的所有事务的 GTID 写入mysql.gtid_executed
表中。gtid_executed
如果为事务分配了 GTID,则通过将 GTID 添加到系统变量 ( ) 中的 GTID 集合,GTID 以非原子方式外部化(事务提交后不久@@GLOBAL.gtid_executed
)。此 GTID 集包含所有已提交 GTID 事务集的表示,并且在复制中用作表示服务器状态的令牌。启用二进制日志记录(根据源代码的要求),gtid_executed
系统变量中的 GTID 集是应用事务的完整记录,但mysql.gtid_executed
表不是,因为最近的历史记录仍在当前二进制日志文件中。在将二进制日志数据传输到副本并存储在副本的中继日志中后(使用已建立的机制进行此过程,请参阅 第 16.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,而是使用存储在中的 GTIDgtid_next
。如果在副本上启用了二进制日志记录,则 GTID 在提交时通过在事务开始时将其写入二进制日志(作为
Gtid_log_event
)以原子方式持久化。每当二进制日志轮换或服务器关闭时,服务器都会将写入先前二进制日志文件的所有事务的 GTID 写入mysql.gtid_executed
表中。如果在副本上禁用了二进制日志记录,GTID 将通过直接写入
mysql.gtid_executed
表中以原子方式持久化。MySQL 向事务附加一条语句以将 GTID 插入表中。在这种情况下,该mysql.gtid_executed
表是对副本应用的事务的完整记录。请注意,在 MySQL 5.7 中,将 GTID 插入表中的操作对于 DML 语句是原子的,但对于 DDL 语句则不是,因此如果服务器在涉及 DDL 语句的事务后意外退出,则 GTID 状态可能会变得不一致。从 MySQL 8.0 开始,该操作对于 DDL 语句和 DML 语句都是原子的。gtid_executed
在副本上提交复制的事务后不久,通过将 GTID 添加到副本的系统变量 (@@GLOBAL.gtid_executed
) 中 的 GTID 集,GTID 以非原子方式外部化 。至于来源,这个 GTID 集包含所有提交的 GTID 事务集的表示。如果副本上禁用了二进制日志记录,则该mysql.gtid_executed
表也是副本上应用的事务的完整记录。如果副本上启用了二进制日志记录,这意味着某些 GTID 仅记录在二进制日志中,gtid_executed
系统变量中的 GTID 集是唯一完整的记录。
在源上完全过滤掉的客户端事务未分配 GTID,因此它们不会添加到
gtid_executed
系统变量中的事务集,或添加到mysql.gtid_executed
表中。但是,在副本上完全过滤掉的复制事务的 GTID 会保留下来。如果在副本上启用了二进制日志记录,则过滤掉的事务将写入二进制日志,Gtid_log_event
后跟一个仅包含BEGIN
和
COMMIT
语句的空事务。如果禁用二进制日志记录,则过滤掉的事务的 GTID 将写入mysql.gtid_executed
表中。为过滤掉的事务保留 GTID 确保
mysql.gtid_executed
表和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。
写入二进制日志的每个数据库更改(DDL 或 DML)都会分配一个 GTID。这包括自动提交的更改,以及使用
BEGIN
和COMMIT
或
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
COMMIT
orXA ROLLBACK
阶段分配了单独的 GTID。XA 事务是持久准备的,以便用户可以提交它们或在发生故障时回滚它们(在复制拓扑中可能包括到另一台服务器的故障转移)。因此,事务的两个部分被单独复制,所以它们必须有自己的 GTID,即使回滚的非 XA 事务不会有 GTID。
在以下特殊情况下,单个语句可以生成多个事务,因此被分配多个 GTID:
调用提交多个事务的存储过程。为过程提交的每个事务生成一个 GTID。
多表
DROP TABLE
语句删除不同类型的表。CREATE TABLE ... SELECT
使用基于行的复制时会发出 一条 语句 (binlog_format=ROW
)。为CREATE TABLE
操作生成一个 GTID,为行插入操作生成一个 GTID。
默认情况下,对于在用户会话中提交的新事务,服务器会自动生成并分配一个新的 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。通过client提交的模拟复制事务与通过replication applier线程提交的复制事务完全等同,事后无法区分。
gtid_purged
系统变量 ( ) 中的 GTID
集
@@GLOBAL.gtid_purged
包含已在服务器上提交的所有事务的 GTID,但不存在于服务器上的任何二进制日志文件中。
gtid_purged
是 的一个子集
gtid_executed
。以下类别的 GTID 位于
gtid_purged
:
在副本上禁用二进制日志记录的情况下提交的复制事务的 GTID。
写入已清除的二进制日志文件的事务的 GTID。
由语句显式添加到集合中的 GTID
SET @@GLOBAL.gtid_purged
。
You can change the value of
gtid_purged
in order to record
on the server that the transactions in a certain GTID set have
been applied, although they do not exist in any binary log on
the server. When you add GTIDs to
gtid_purged
, they are also
added to gtid_executed
. An
example use case for this action is when you are restoring a
backup of one or more databases on a server, but you do not have
the relevant binary logs containing the transactions on the
server. In MySQL 5.7, you can only change the value of
gtid_purged
when
gtid_executed
(and therefore
gtid_purged
) is empty. For
details of how to do this, see the description for
gtid_purged
.
The sets of GTIDs in the
gtid_executed
and
gtid_purged
system variables
are initialized when the server starts. Every binary log file
begins with the event
Previous_gtids_log_event
, which contains the
set of GTIDs in all previous binary log files (composed from the
GTIDs in the preceding file's
Previous_gtids_log_event
, and the GTIDs of
every Gtid_log_event
in the preceding file
itself). The contents of
Previous_gtids_log_event
in the oldest and
most recent binary log files are used to compute the
gtid_executed
and
gtid_purged
sets at server
startup:
gtid_executed
is computed as the union of the GTIDs inPrevious_gtids_log_event
in the most recent binary log file, the GTIDs of transactions in that binary log file, and the GTIDs stored in themysql.gtid_executed
table. This GTID set contains all the GTIDs that have been used (or added explicitly togtid_purged
) on the server, whether or not they are currently in a binary log file on the server. It does not include the GTIDs for transactions that are currently being processed on the server (@@GLOBAL.gtid_owned
).gtid_purged
is computed by first adding the GTIDs inPrevious_gtids_log_event
in the most recent binary log file and the GTIDs of transactions in that binary log file. This step gives the set of GTIDs that are currently, or were once, recorded in a binary log on the server (gtids_in_binlog
). Next, the GTIDs inPrevious_gtids_log_event
in the oldest binary log file are subtracted fromgtids_in_binlog
. This step gives the set of GTIDs that are currently recorded in a binary log on the server (gtids_in_binlog_not_purged
). Finally,gtids_in_binlog_not_purged
is subtracted fromgtid_executed
. The result is the set of GTIDs that have been used on the server, but are not currently recorded in a binary log file on the server, and this result is used to initializegtid_purged
.
If binary logs from MySQL 5.7.7 or older are involved in these
computations, it is possible for incorrect GTID sets to be
computed for gtid_executed
and
gtid_purged
, and they remain
incorrect even if the server is later restarted. For details,
see the description for the
binlog_gtid_simple_recovery
system variable, which controls how the binary logs are iterated
to compute the GTID sets. If one of the situations described
there applies on a server, set
binlog_gtid_simple_recovery=FALSE
in the server's configuration file before starting it. That
setting makes the server iterate all the binary log files (not
just the newest and oldest) to find where GTID events start to
appear. This process could take a long time if the server has a
large number of binary log files without GTID events.
If you need to reset the GTID execution history on a server, use
the RESET MASTER
statement. For
example, you might need to do this after carrying out test
queries to verify a replication setup on new GTID-enabled
servers, or when you want to join a new server to a replication
group but it contains some unwanted local transactions that are
not accepted by Group Replication.
Use RESET MASTER
with caution
to avoid losing any wanted GTID execution history and binary
log files.
Before issuing RESET MASTER
,
ensure that you have backups of the server's binary log files
and binary log index file, if any, and obtain and save the GTID
set held in the global value of the
gtid_executed
system variable
(for example, by issuing a SELECT
@@GLOBAL.gtid_executed
statement and saving the
results). If you are removing unwanted transactions from that
GTID set, use mysqlbinlog to examine the
contents of the transactions to ensure that they have no value,
contain no data that must be saved or replicated, and did not
result in data changes on the server.
When you issue RESET MASTER
, the
following reset operations are carried out:
The value of the
gtid_purged
system variable is set to an empty string (''
).The global value (but not the session value) of the
gtid_executed
system variable is set to an empty string.The
mysql.gtid_executed
table is cleared (see mysql.gtid_executed Table).If the server has binary logging enabled, the existing binary log files are deleted and the binary log index file is cleared.
请注意,这RESET MASTER
是重置 GTID 执行历史记录的方法,即使服务器是禁用二进制日志记录的副本。
RESET SLAVE
对 GTID 执行历史没有影响。