Documentation Home

16.1.3.1 GTID 格式和存储

全局事务标识符 (GTID) 是创建的唯一标识符,并与在原始服务器(源)上提交的每个事务相关联。此标识符不仅对于它起源的服务器是唯一的,而且在给定复制拓扑中的所有服务器中也是唯一的。

GTID 分配区分在源上提交的客户端事务和在副本上复制的复制事务。当客户端事务在源上提交时,它会被分配一个新的 GTID,前提是该事务已写入二进制日志。保证客户端事务具有单调递增的 GTID,生成的数字之间没有间隙。如果客户端事务未写入二进制日志(例如,因为事务被过滤掉,或者事务是只读的),则不会在源服务器上为其分配 GTID。

复制事务保留分配给源服务器上事务的相同 GTID。GTID 在复制事务开始执行之前就存在,即使复制事务没有写入副本上的二进制日志,或者在副本上被过滤掉,它也会被持久化。MySQL 系统表mysql.gtid_executed用于保存 MySQL 服务器上应用的所有事务的分配 GTID,但存储在当前活动二进制日志文件中的事务除外。

GTID 的自动跳过功能意味着在源上提交的事务只能在副本上应用一次,这有助于保证一致性。一旦在给定服务器上提交了具有给定 GTID 的事务,任何试图执行具有相同 GTID 的后续事务的尝试都会被该服务器忽略。没有引发错误,也没有执行事务中的语句。

如果具有给定 GTID 的事务已开始在服务器上执行,但尚未提交或回滚,则任何在具有相同 GTID 的服务器上启动并发事务的尝试都会被阻止。服务器既不开始执行并发事务也不将控制权返回给客户端。一旦事务的第一次尝试提交或回滚,阻塞在同一 GTID 上的并发会话可能会继续。如果第一次尝试回滚,一个并发会话将继续尝试事务,并且在同一 GTID 上阻塞的任何其他并发会话将保持阻塞状态。如果第一次尝试提交,则所有并发会话停止阻塞,并自动跳过该事务的所有语句。

GTID 表示为一对坐标,由冒号字符 ( :) 分隔,如下所示:

GTID = source_id:transaction_id

source_id标识原始服务器 。通常,源的 server_uuid用于此目的。这transaction_id是一个序列号,由在源上提交事务的顺序确定。例如,要提交的第一个事务的值为1transaction_id而在同一原始服务器上要提交的第十个事务的transaction_id值为 10。事务不可能0在 GTID 中具有序列号。例如,最初在具有 UUID 的服务器上要提交的第 23 个事务 3E11FA47-71CA-11E1-9E33-C80AA9429562具有此 GTID:

3E11FA47-71CA-11E1-9E33-C80AA9429562:23

服务器实例上 GTID 序列号的上限是带符号的 64 位整数的非负值数(2 的 63 次方减去 1,或 9,223,372,036,854,775,807)。如果服务器用完了 GTID,它会采取 指定的操作 binlog_error_action

事务的 GTID 显示在 mysqlbinlog的输出中,它用于标识 Performance Schema 复制状态表中的单个事务,例如, replication_applier_status_by_workergtid_next 系统变量 ( )存储的值@@GLOBAL.gtid_next是单个 GTID。

GTID 集

GTID 集是包含一个或多个单个 GTID 或 GTID 范围的集合。GTID 集在 MySQL 服务器中以多种方式使用。例如, gtid_executedgtid_purged系统变量存储的值是 GTID 集合。START SLAVE 子句UNTIL SQL_BEFORE_GTIDSUNTIL SQL_AFTER_GTIDS可用于使副本处理事务仅达到 GTID 集中的第一个 GTID,或者在 GTID 集中的最后一个 GTID 之后停止。内置函数需要GTID_SUBSET()GTID GTID_SUBTRACT()集作为输入。

来自同一服务器的一系列 GTID 可以折叠成一个表达式,如下所示:

3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5

server_uuid上面的示例表示 第一个到第五个事务源自 MySQL 服务器 3E11FA47-71CA-11E1-9E33-C80AA9429562。来自同一服务器的多个单个 GTID 或 GTID 范围也可以包含在单个表达式中,GTID 或范围用冒号分隔,如以下示例所示:

3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49

GTID 集可以包括单个 GTID 和 GTID 范围的任意组合,并且它可以包括来自不同服务器的 GTID。此示例显示存储在 已应用来自多个源的事务的副本 的gtid_executed系统变量 ( ) 中的 GTID 集:@@GLOBAL.gtid_executed

2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19

从服务器变量返回 GTID 集时,UUID 按字母顺序排列,数字间隔合并并按升序排列。

GTID 集的语法如下:

gtid_set:
    uuid_set [, uuid_set] ...
    | ''

uuid_set:
    uuid:interval[:interval]...

uuid:
    hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh

h:
    [0-9|A-F]

interval:
    n[-n]

    (n >= 1)
mysql.gtid_executed 表

GTID 存储在数据库中名为 的表 gtid_executedmysql。此表中的一行包含,对于它表示的每个 GTID 或一组 GTID,原始服务器的 UUID,以及该组的开始和结束事务 ID;对于仅引用单个 GTID 的行,最后两个值相同。

mysql.gtid_executed表是在安装或升级 MySQL 服务器时创建的(如果它尚不存在),使用CREATE TABLE 类似于此处显示的语句:

CREATE TABLE gtid_executed (
    source_uuid CHAR(36) NOT NULL,
    interval_start BIGINT(20) NOT NULL,
    interval_end BIGINT(20) NOT NULL,
    PRIMARY KEY (source_uuid, interval_start)
)
警告

与其他 MySQL 系统表一样,不要尝试自己创建或修改此表。

mysql.gtid_executed表供 MySQL 服务器内部使用。当副本上的二进制日志记录被禁用时,它使副本能够使用 GTID,并且当二进制日志丢失时它能够保留 GTID 状态。请注意,mysql.gtid_executed 如果您发出 ,该表将被清除RESET MASTER

mysql.gtid_executed 只有当gtid_modeON或时 ,GTID 才会存储在表中ON_PERMISSIVE。GTID 的存储点取决于二进制日志记录是启用还是禁用:

  • 如果禁用二进制日志记录(log_binis OFF),或者 log_slave_updates禁用,则服务器将属于每个事务的 GTID 与表中的事务一起存储。此外,该表以用户可配置的速率定期压缩;有关详细信息,请参阅 mysql.gtid_executed 表压缩。这种情况仅适用于禁用二进制日志记录或副本更新日志记录的副本。它不适用于复制源服务器,因为在源服务器上,必须启用二进制日志记录才能进行复制。

  • 如果启用了二进制日志记录(log_binON),则每当二进制日志轮换或服务器关闭时,服务器都会将写入先前二进制日志的所有事务的 GTID 写入mysql.gtid_executed表中。这种情况适用于复制源服务器或启用了二进制日志记录的副本。

    在服务器意外停止的情况下,当前二进制日志文件中的 GTID 集不会保存在 mysql.gtid_executed表中。这些 GTID 在恢复期间从二进制日志文件添加到表中。例外情况是在服务器重新启动时未启用二进制日志记录。在这种情况下,服务器无法访问二进制日志文件来恢复 GTID,因此无法启动复制。

    启用二进制日志记录后,该 mysql.gtid_executed表不会保存所有已执行事务的 GTID 的完整记录。该信息由 gtid_executed系统变量的全局值提供。始终使用 @@GLOBAL.gtid_executed,它在每次提交后更新,代表 MySQL 服务器的 GTID 状态,并且不要查询 mysql.gtid_executed表。

mysql.gtid_executed 表压缩

随着时间的推移,该 mysql.gtid_executed表可能会充满许多行,这些行引用源自同一服务器的各个 GTID,并且其事务 ID 构成一个范围,类似于此处显示的内容:

+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37             | 37           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38             | 38           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39             | 39           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40             | 40           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41             | 41           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42             | 42           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43             | 43           |
...

为了节省空间,MySQL 服务器 mysql.gtid_executed通过用跨越整个事务标识符间隔的单个行替换每个这样的行集来定期压缩表,如下所示:

+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37             | 43           |
...

gtid_executed_compression_period 您可以通过设置系统变量 来控制在压缩表之前允许经过的事务数,从而控制压缩率 。此变量的默认值为 1000,这意味着默认情况下,每 1000 个事务后执行一次表压缩。设置 gtid_executed_compression_period 为 0 会完全阻止执行压缩, gtid_executed如果您这样做,您应该做好准备,因为表可能需要的磁盘空间量可能会大幅增加。

笔记

启用二进制日志记录时, 使用 的值,并且 gtid_executed_compression_period 在每次二进制日志轮换时压缩表。 mysql.gtid_executed

表的压缩mysql.gtid_executed由名为 的专用前台线程执行 thread/sql/compress_gtid_table。该线程未列在 的输出中SHOW PROCESSLIST,但可以将其视为 threads表中的一行,如下所示:

mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
          THREAD_ID: 26
               NAME: thread/sql/compress_gtid_table
               TYPE: FOREGROUND
     PROCESSLIST_ID: 1
   PROCESSLIST_USER: NULL
   PROCESSLIST_HOST: NULL
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
   PROCESSLIST_TIME: 1509
  PROCESSLIST_STATE: Suspending
   PROCESSLIST_INFO: NULL
   PARENT_THREAD_ID: 1
               ROLE: NULL
       INSTRUMENTED: YES
            HISTORY: YES
    CONNECTION_TYPE: NULL
       THREAD_OS_ID: 18677

thread/sql/compress_gtid_table线程通常会休眠直到 gtid_executed_compression_period 事务被执行,然后醒来以执行表的压缩, 如前所述mysql.gtid_executed。然后它会休眠直到 gtid_executed_compression_period 发生另一个事务,然后醒来再次执行压缩,无限期地重复此循环。当禁用二进制日志记录时将此值设置为 0 意味着线程始终处于休眠状态并且永远不会醒来,这意味着不使用这种显式压缩方法。相反,压缩会根据需要隐式发生。