GTID生命周期
GTID的生命周期包括以下步骤:
- 事务在主服务器上执行并提交。此客户端事务分配有一个GTID,该ID由主服务器的UUID和此服务器上尚未使用的最小非零事务序列号组成。GTID将写入主数据库的二进制日志中(紧接在日志中事务本身之前)。如果没有将客户事务写入二进制日志中(例如,由于该事务已被滤除或该事务为只读),则不会为其分配GTID。
- 如果为事务分配了GTID,则在提交时通过在事务开始时将GTID写入二进制日志中(作为
Gtid_log_event
)将GTID自动保留。每当旋转二进制日志或关闭服务器时,服务器都会将写入前一个二进制日志文件的所有事务的GTID写入mysql.gtid_executed
表中。 - 如果为交易分配了GTID,则将GTID添加到
gtid_executed
系统变量(@@GLOBAL.gtid_executed
)中的GTID集中,从而以非原子方式(在提交交易后不久)将GTID外部化。此GTID集包含所有已提交GTID事务集的表示,并且在复制中用作表示服务器状态的令牌。启用二进制日志记录(对于主数据库而言是必需的)后,gtid_executed
系统变量中的GTID集是所应用事务的完整记录,但该mysql.gtid_executed
表不是完整记录,因为最新历史记录仍在当前二进制日志文件中。 - 在将二进制日志数据传输到从站并存储在从站的中继日志中之后(使用此过程使用的已建立机制,有关详细信息,请参见“复制实现的详细信息”),从站将读取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自动保留(作为
Gtid_log_event
)。每当旋转二进制日志或关闭服务器时,服务器都会将写入前一个二进制日志文件的所有事务的GTID写入mysql.gtid_executed
表中。 - 如果在从属服务器上禁用了二进制日志记录,则将GTID直接写入
mysql.gtid_executed
表中可以自动保留GTID 。MySQL将一个语句添加到事务中,以将GTID插入表中。从MySQL 8.0开始,此操作对于DDL语句和DML语句都是原子的。在这种情况下,该mysql.gtid_executed
表是在从属服务器上应用的事务的完整记录。 - 在将复制事务提交到从属服务器上后不久,通过将其添加到从属服务器的
gtid_executed
系统变量(@@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集可以压缩。如“ 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。这包括自动提交的更改,以及使用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交易分配了单独的GTID,分别用于XA PREPARE
交易的阶段和交易的XA COMMIT
or XA ROLLBACK
阶段。XA事务是持久准备的,因此用户可以在发生故障(在复制拓扑中可能包括故障转移到另一台服务器)的情况下提交或回滚它们。因此,事务的两个部分是分别复制的,因此,即使回滚的非XA事务不具有GTID,它们也必须具有自己的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
系统变量
该集在GTIDs的gtid_purged
系统变量(@@GLOBAL.gtid_purged
)包含所有已提交服务器上的交易GTIDs,但在任何服务器上的二进制日志文件不存在。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_purged
when gtid_executed
(因此gtid_purged
)为空。在MySQL 8.0中,此限制不适用,您还可以选择是gtid_purged
使用指定的GTID集替换整个 GTID集,还是将指定的GTID集添加到已经存在的GTID中gtid_purged
。有关如何执行此操作的详细信息,请参见的说明gtid_purged
。
服务器启动时,gtid_executed
和gtid_purged
系统变量中的GTID集将初始化。每个二进制日志文件都以event开头,该事件Previous_gtids_log_event
包含所有先前二进制日志文件中的GTID集(由先前文件中的Previous_gtids_log_event
GTID和Gtid_log_event
先前文件本身中的每个GTID组成)。的内容Previous_gtids_log_event
在最早和最近的二进制日志文件来计算的gtid_executed
,并gtid_purged
套在服务器启动时:
gtid_executed
计算为Previous_gtids_log_event
最近的二进制日志文件中的GTID,该二进制日志文件中的事务的GTID和mysql.gtid_executed
表中存储的GTID的并集。该GTID集包含gtid_purged
服务器上已经使用(或显式添加到)的所有GTID ,无论它们当前是否在服务器上的二进制日志文件中。它不包含服务器(@@GLOBAL.gtid_owned
)当前正在处理的交易的GTID 。gtid_purged
通过首先将Previous_gtids_log_event
最新二进制日志文件中的GTID与该二进制日志文件中的事务的GTID 相加来计算。该步骤给出了当前或曾经在服务器上的二进制日志中记录的一组GTIDgtids_in_binlog
。接下来,从中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 SLAVE
对GTID执行历史没有影响。