1. 什么是GTID
从MySQL 5.6.5 开始新增了一种基于 GTID 的复制方式。GTID (Global Transaction ID)是全局事务ID,由主库上生成的与事务绑定的唯一标识,这个标识不仅在主库上是唯一的,在MySQL复制环境内也是唯一的。
2. GTID格式及含义
GTID = source_uuid:transaction_id
source_uuid是发起事务服务器的server_uuid,是一个MySQL实例的唯一标识,可以通过show variables like 'server_uuid'
查看。transaction_id代表了该实例上已经提交的事务数量,是一个单调递增的序列号,从1开始,1-2代表第二个事务;第1-n代表n个事务。
GTID复制拓扑中,无论事务被复制了多少次,事务的GTID都保持不变。一旦某个事务在服务器上提交后,后续所有相同GTID的事务都会被忽略,这种机制可以有效避免重复复制的现象,保持数据一致。
示例1:7a7f2A47-71CA-11E1-9E33-00163e429562:1
含义: 7a7f2A47-71CA-11E1-9E33-00163e429562 是这个节点的server_uuid,1为这个节点上提交的第1个事务的事务号,如果提交了10个事务,GTID会是: 7a7f2A47-71CA-11E1-9E33-00163e429562:1-10
【如果有多个范围要表示,则事务编号的范围可以用冒号分隔】
示例2:7a7f2A47-71CA-11E1-9E33-00163e429562:1-3:11:47-49
含义:执行了源自7a7f2A47-71CA-11E1-9E33-00163e429562服务器的第1-3个事务、第11个事务、第47-49个事务
【GTID集还能表示来自不同数据源的事务集】
示例3:7a7f2A47-71CA-11E1-9E33-00163e429562:1-77439,82b6d927-81cc-11ee-9edf-00163e202091:1-98191
含义:执行了源自7a7f2A47-71CA-11E1-9E33-00163e429562服务器的第1-77439个事务和源自82b6d927-81cc-11ee-9edf-00163e202091服务器的第1-98191个事务
MySQL 8.3 创新版于 2024 年 1 月 16 号发布,该版本扩展了 MySQL
复制和组复制中使用全局事务标识(GTID)的格式,支持给 GTID 打标签,以支持识别事务组。此增强功能可以为特定事务组的 GTID
分配唯一标识。例如:包含数据操作的事务可以很容易地与管理操作产生的事务区分开来,只需要比较他们的 GTID。
带标签的格式
扩展后的 GTID 格式是source_id:<tag>:transaction_id
,其中 tag 是最长为 8 个字符的任意字符串。通过设置系统变量 gtid_next 的值为 automatic: 启用,或者设置 gtid_next 为 uuid::transaction_id 以将单个事务的 uuid 设置为任意值,并为其分配自定义标签。
3. GTID相关参数
查看关于gtid的系统参数:
mysql> show variables like '%gtid%'; +-----------------------------------+-----------------------------------------------+ | Variable_name | Value | +-----------------------------------+-----------------------------------------------+ | binlog_gtid_simple_recovery | ON | | enforce_gtid_consistency | ON | | gtid_executed | be423c64-90c8-11ed-bf93-00163e0fcdea:1-1169392| | gtid_executed_compression_period | 1000 | | gtid_mode | ON | | gtid_next | AUTOMATIC | | gtid_owned | | | gtid_purged | be423c64-90c8-11ed-bf93-00163e0fcdea:1-1137671| | session_track_gtids | OFF | +-----------------------------------+-----------------------------------------------+ 9 row in set (0.00 sec)
3.1 gtid_mode
【作用】控制是否开启GTID模式的日志记录及日志内可以包含的事务类型
【可选值】
OFF
:禁用GTID主从复制,新增和复制的事务都必须是匿名事务。ON
:启用GTID主从复制,但不会自动创建GTID集。新增和复制的事务都必须是GTID事务。ON_PERMISSIVE
:启用GTID主从复制,并允许从库自动创建GTID集。新增的事务是GTID事务,复制的事务可以是匿名事务或GTID事务。(这个选项在标准的MySQL版本中并不直接支持)ON_ENFORCING
:启用GTID主从复制,并要求从库手动创建GTID集。
3.2 enforce_gtid_consistency
【作用】指定从库执行事务时的一致性要求,控制SQL语句是否仅允许违反GTID一致性
【可选值】
- OFF:不要求从库执行事务时的一致性要求。
- ON:要求从库执行事务时的一致性要求。(通常设置为ON以确保一致性)
- WARN:允许违反GTID一致性,但是会生成告警信息。
开启该参数服务器仅允许GTID安全的语句执行,防止某些不安全的语句导致GTID复制失败。在启用GTID模式复制前,必须设置为true。当设置为true以后,以下操作将不再可用:
- create table … select语句(MySQL8.0.21后,如果引擎支持原子DDL,可以使用此语句)。
- 事务内的create temporary table和drop temporary table。
- 同时更新事务表和非事务表的事务或语句。
enforce_gtid_consistency验证语句的GTID兼容性是在写二进制日志的时候,如果在服务器上禁用了二进制日志或过滤器删除了语句而未写入二进制日志,都不会检查。
3.3 gtid_executed
全局变量(@@global.gtid_executed
),包含服务器执行的所有事务的GTID集和gtid_purged被设置的变量。@@global.gtid_executed
和show master status
或show slave status
语句输出executed_gtid_set
字段的值相同。
当服务器启动时,会根据binlog_gtid_simple_reocovery
参数选择不同的方法初始化其值。
3.4 gtid_next
gtid_next
是会话级变量,代表下一个事务即将分配的GTID。当事务在主库提交时,系统会自动分配一个新的GTID,当事务复制到从库时,会保留主库分配的GTID,而不是由从库分配新的GTID。GTID可以设置为下列值:
automatic
:系统为事务自动生成GTIDanonymous
:事务没有GTID(匿名事务),只能通过文件名和偏移量定位UUID:NUMBER
:明确设定一个有效的GTID,下一个事务将分配这个GTID。
此参数的功能还受到gtid_mode
参数的影响,如果gtid_mode
设置为OFF
(只有匿名事务),那么此参数无效。
【注意】如果用SERVER_UUID:TRANSACTION_NUMBER
手动为事务分配了GTID,无论事务提交或回滚,在开启下一个事务前,必须再次手动设置gtid_next
的值或设置为automatic
。
3.5 gtid_purged
gtid_purged
是全局变量,代表所有在服务器上已提交,但是不在二进制日志中的事务的GTID集。gtid_purged
是gtid_exeucted
的子集。
gtid_purged中的GTID包含下列类别:
- 从库禁用binarylog,则提交后的事务GTID也会加入gtid_purged。
- 从库启用binarylog,事务也写入二进制日志,但是日志文件被purged(删除)掉了。
- 通过
set @@global.gtid_purged
语句显示加入到集合中。
gtid_purged
和gtid_executed
一样,会在服务器启动或重启时根据binlog_gtid_simple_reocovery
参数选择不同的方法初始化其值。
如果要求过滤某些事务,可以手动设置gtid_purged值,这些值会被加入gtid_executed,即使这些事务从未执行。
gtid_purged设置方式有2种:
SET @@global.gtid_purged = 'gtid_set'
– 替换SET @@global.gtid_purged = '+gtid_set'
– 追加
binlog_gtid_simple_reocovery
控制MySQL启动或重启时如何检索二进制日志来计算gtid_executed
和gtid_purged
变量的初始值。当binlog_gtid_simple_recovery=true
时,只读取最旧和最新的binlog文件,否则遍历所有binlog文件。
3.6 gtid_owned
表示当前正在执行的事务的gtid。全局或会话级只读变量,通常只内部使用(其输出可能对于普通用户来说不是非常直观),帮助MySQL管理和跟踪当前正在处理的事务。
- 全局级:包含服务器正在使用的所有GTID列表和对应的Thread ID。
- 会话级:包含会话当前正在使用的单个GTID。
由于它是全局和会话级别的,其显示的内容可能会根据当前的会话和全局状态而有所不同。
3.7 gtid_executed_compression_period
控制压缩mysql.gtid_executed
表的事务阈值数量。
mysql> show variables like 'gtid_executed_compression_period'; +-----------------------------------+---------+ | Variable_name | Value | +-----------------------------------+---------+ | gtid_executed_compression_period | 1000 | +-----------------------------------+---------+ 1 row in set (0.00 sec)
该参数默认值为1000,指在每执行1000次事务后执行gtid_executed表的压缩,方法是将source_id一样的事务进行合并(仅二进制日志未开启时生效)。二进制日志开启时,此参数无效,只有在二进制日志轮换时才进行压缩。
3.8 binlog_gtid_simple_recovery
binlog_gtid_simple_recovery
是MySQL中一个重要的系统变量,它用于控制在MySQL启动或重启时,如何遍历二进制日志文件(binlog)以寻找全局事务标识符(GTID)。这个变量对于使用GTID进行复制的环境尤为重要,因为它影响了MySQL如何初始化和验证GTID集合。
【版本引入】在MySQL 5.7.5版本中,这个变量以simplified_binlog_gtid_recovery
的形式被引入;从MySQL 5.7.6版本开始,它被重命名为binlog_gtid_simple_recovery
。
- 当
binlog_gtid_simple_recovery=FALSE
时:
gtid_executed
的初始化:MySQL会从最新的binlog文件开始,逆向遍历到第一个包含Previous_gtids_log_event
或Gtid_log_event
的文件。从该文件中读取所有的GTID,并存储在内部变量中,然后计算gtid_executed
的值。gtid_purged
的初始化:MySQL会从最早的binlog文件开始,正向遍历到第一个包含非空的Previous_gtids_log_event
或至少一个Gtid_log_event
的文件。根据这些事件计算gtid_purged
的值。- 【性能影响】:如果binlog文件数量很大且没有GTID事件,这个过程可能会非常耗时。
- 当
binlog_gtid_simple_recovery=TRUE
时(MySQL 5.7.7及以后版本的默认值):gtid_executed
和gtid_purged
的初始化:MySQL只遍历最新的和最早的binlog文件,并根据这些文件中的Previous_gtids_log_event
或Gtid_log_event
来计算gtid_executed
和gtid_purged
的值。
【性能优势】:这种方法显著减少了遍历binlog文件的数量,从而提高了MySQL启动和重启时的性能。
3.9 session_track_gtids
session_track_gtids
是 MySQL 中的一个系统变量,用于控制 MySQL 会话(session)中是否追踪全局事务标识符(GTID)的状态。这个变量对于使用 GTID 进行复制和读写分离的 MySQL 环境尤为重要,因为它可以帮助确保数据的一致性和准确性。
【可选值】:
OFF
:默认值,表示不追踪 GTID。在这种模式下,MySQL 会话不会记录与 GTID 相关的状态信息。OWN_GTID
:表示追踪上次反馈后当前会话所提交的所有新增 GTID。这个选项主要用于跟踪会话内部产生的 GTID,以便在需要时能够准确地识别出哪些 GTID 是由当前会话产生的。ALL_GTIDS
:表示直接返回gtid_executed
系统变量的值,即在当前会话事务完成提交后读取所有已执行的 GTID。这个选项提供了更全面的 GTID 追踪能力,但可能会引入额外的性能开销(由于需要读取和记录所有已执行的 GTID,可能会增加额外的 CPU 和内存开销)。
【配置建议】在大多数情况下,如果不需要进行复杂的读写分离或数据一致性校验,可以保持其默认值 OFF
以减少性能开销。如果需要这些功能,则可以根据实际情况选择 OWN_GTID
或 ALL_GTIDS
。
3.10 其他相关参数
虽然不直接与GTID的配置相关,但以下参数在配置GTID主从复制时也非常重要:
server-id
:指定MySQL服务器的唯一ID号,每个MySQL服务器(包括主库和从库)都必须拥有不同的ID号。log-bin
:启用二进制日志功能,这是MySQL主从复制的基础。binlog_format
:指定二进制日志的格式,对于GTID主从复制,通常设置为ROW以记录行级别的更改操作。
4. GTID生命周期
4.1 GTID的生成与分配
- 事务提交与GTID分配:当一个事务在主库上执行并提交时,MySQL会自动为该事务分配一个GTID。
- 写入二进制日志:分配GTID后,该GTID会在事务提交时以Gtid_log_event事件的形式(将GTID和事务本身)写入主库的二进制日志文件中。
这是一个原子操作保证GTID和事务内容同时写入日志。
4.2 2. GTID的持久化与记录
事务提交后很短的时间内,将GTID写入@@global.gtid_executed状态变量。此变量包含了所有已执行的事务,代表了主库的当前状态。
- 写入
mysql.gtid_executed
表:当二进制日志发生切换或MySQL服务器关闭时,所有已写入二进制日志的GTID会被写入到mysql.gtid_executed
表中,以实现GTID的持久化。 - 更新
GLOBAL.gtid_executed
系统变量:在事务提交时,MySQL还会将该GTID加入到@@GLOBAL.gtid_executed
系统变量中。这个变量是一个GTID集合,代表了目前为止所有被执行过的事务。
4.3 GTID的传输与应用
- 传输到从库:在主从复制过程中,包含GTID的二进制日志会被传输到从库,并存储在从库的中继日志(
relay log
)中。 - 从库应用GTID事务:从库读取relay日志中的GTID,并将其
gtid_next
变量设置为该GTID,表示从库下一个即将执行的事务。然后,从库执行该GTID对应的事务,执行前会先进行检查,保证此GTID没有被执行,且当前的没有其他会话读取此GTID。
如果有多个会话读取了此GTID,只有1个可以执行,其他的都会阻塞。从库的gtid_owned
系统变量@@global.gtid_owned
显示当前正在使用的GTID以及拥有它的线程ID。如果已经使用了该GTID,通过自动跳过功能忽略该事务,并且不会引发错误。
如果此GITD未执行过,从库应用此事务,并使用主库生成的GTID,不会重新分配。 - 从库记录GTID:如果从库启用了二进制日志功能,它也会通过gtid_log_event原子事件将该GTID写入到自己的二进制日志文件中。如果未启用,当轮换日志或关闭服务器时,则GTID会被记录在
mysql.gtid_executed
表中(开启二进制日志时mysql.gtid_executed
不代表服务器的最新状态)。
从库提交后很短的时间内,将GTID写入从库@@global.gtid_executed
状态变量(建议始终查询@@global.gtid_executed
来查看服务器的状态)
4.4 GTID的清理与重置
- 清理GTID_PURGED:如果二进制日志被删除,则删除部分的GTID信息会被更新到
@@GLOBAL.GTID_PURGED
中。这个变量是@@GLOBAL.gtid_executed
的子集,表示已经被清理的GTID集合。 - 重置GTID信息:使用
RESET MASTER
命令会清除@@global.gtid_purged
、@@global.gtid_executed
、mysql.gtid_executed
表以及清理binlog。
而RESET SLAVE
和RESET SLAVE ALL
命令会清理掉@@global.gtid_executed
和@@global.gtid_purged
。
4.5 GTID的并行复制
多线程复制:如果启用了多线程复制(slave_parallel_workers > 0
),MySQL会并行执行GTID事务。此时,gtid_executed
变量的值可能会有GAP,系统会自动更新这些值以反映并行执行的状态。
5. GTID自动定位
GTID自动定位是MySQL复制中的一个重要特性,它允许副本(slave)自动定位到主库(master)的正确位置进行数据同步,而无需手动指定日志文件和位置,极大地简化了主从复制的管理和切换过程。
在使用GTID进行复制时,副本通过启用SOURCE_AUTO_POSITION或MASTER_AUTO_POSITION选项来自动连接到主库。这个选项允许副本在初始握手时发送一个包含已接收、提交或两者兼有的事务的GTID集合,从而使得副本能够自动定位到正确的位置进行数据同步。
CHANGE MASTER TO MASTER_HOST='master_host_name', MASTER_USER='replication_user_name', MASTER_PASSWORD='replication_password', MASTER_AUTO_POSITION = 1;
MASTER_AUTO_POSITION = 1 表示slave应该自动定位到master的GTID。
如果已经有一个配置好的slave,并且想要开启或者更改GTID自动定位,可以使用以下命令:
STOP SLAVE; CHANGE MASTER TO MASTER_AUTO_POSITION = 1; START SLAVE;
6. GTID复制监控与管理
6.1 mysql.gtid_executed表
mysql.gtid_executed
表记录了是服务器上已经执行事务的GTID。表的结构如下:
+------------------+-------------+------+-------+------------+-----------+ | Field | Type | Null | Key | Default | Extra | +------------------+-------------+------+-------+------------+-----------+ | source_uuid | char(36) | NO | PRI | NULL | | +------------------+-------------+------+-------+------------+-----------+ | interval_start | bigint | NO | PRI | NULL | | +------------------+-------------+------+-------+------------+-----------+ | interval_end | bigint | NO | | NULL | | +------------------+-------------+------+-------+------------+-----------+ 3 rows in set (0.00 sec)
因此当启用二进制日志记录时,mysql.gtid_executed
表并不保存最新已执行事务GTID,最新状态可以由gtid_executed
全局系统变量(@@global.gtid_executed
)提供,该变量在每次提交事务后更新。为了节省空间,MySQL服务器会定期压缩mysql.gtid_executed
表,方法是将多行表示为单行GTID集。
6.2 监控复制心跳
复制建立后,主库会定期向从库发送心跳信号(即使没有发送事务),心跳可以在建立主从复制时,通过change master to的master_heartbeat_period
子句指定。如果未指定则默认是从库连接超时的一半:
mysql> show variables like 'slave_net_timeout'; +-------------------+---------+ | Variable_name | Value | +-------------------+---------+ | slave_net_timeout | 10 | +-------------------+---------+ 1 row in set (0.00 sec)
最近一次的心跳时间可以通过performance_schema.replication_connection_status
表中的last_heartbeat_timestamp
查看