1.1 GTID的概念
GTID(全局事务标识符)是mysql MySQL-5.6.5开始支持的新特性之一,全局事务标识符不仅在源(主)服务器上是唯一的,而且在给定复制设置中的所有服务器都是唯一的。正因为这样一个特性使得mysql的主从复制变得更加简单且一致性更高。它与源(主)服务器上提交的每个事务相关联,由服务器ID+事务ID组合而成。
GTID = source_id:transaction_id,中间由“:”分隔,source_id用于标识原服务器,通常指server_uuid,由于GTID会传递到slave,所以也可以理解为源ID。transaction_id为当前服务器上已提交事务的一个序列号,通常从1开始自增长的序列,一个数值对应一个事务。
查看本机的server_uuid方法如下:
- mysql> show variables like '%uuid%';
- +---------------+--------------------------------------+
- | Variable_name | Value |
- +---------------+--------------------------------------+
- | server_uuid | f3d0a8b5-a657-11eb-a6e5-000c29dbd935 |
- +---------------+--------------------------------------+
- 1 row in set (0.01 sec)
1.2 GTID的工作原理
当一个事务在主库端执行并提交时,会产生一个GTID并记录到binlog日志中。
binlog传输到slave,并存储到slave的relaylog后,读取这个GTID的这个值设置gtid_next变量,即告诉Slave,下一个要执行的GTID值。
sql线程从relay log中获取GTID,然后对比slave端的binlog是否有该GTID,如果有说明该GTID的事务已经执行,slave会忽略。如果没有记录,slave会执行该GTID的事务,在执行前会检查其他session持有该GTID,确保该GTID的事务不会被重复执行,并记录该GTID到自身的binlog。
在解析过程中会判断是否有主键,如果没有就用二级索引,再没有就走全表扫描。
1.3 mysql.gtid_exected表
GTID存储在mysql的 gtid_executed的表中。该表供mysql服务器内部使用,该表中的一行代表的每个GTID或GTID集合,以及该集合的开始和结束事务ID;对于仅引用单个GTID的行,最后两个值相同。
在mysql.gtid_executed安装或升级MySQL Server时,使用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系统表一样,请勿尝试自己创建或修改该表。
GTID仅当gtid_mode is ON or ON_PERMISSIVE时,GTID才会存储在gtid_executed的表中,GTID的存储与mysql是否启用二进制日志紧密相关。
- 如果禁用了二进制日志记录(log_binis OFF),或者如果 log_slave_updates禁用了二进制日志记录,则服务器将属于每个事务的GTID与该事务一起存储在表中。此外,该表会以用户可配置的速率定期压缩。这种情况仅适用于禁用了二进制日志记录或副本更新日志记录的副本。它不适用于复制源服务器,因为必须在源上启用二进制日志记录才能进行复制。
- 如果启用了二进制日志记录(log_bin是 ON),则每当旋转二进制日志或关闭服务器时,服务器都会将写入前一个二进制日志的所有事务的GTID写入mysql.gtid_executed表中。这种情况适用于复制源服务器或启用了二进制日志记录的副本。
- 如果服务器意外停止,则当前二进制日志文件中的GTID集不会保存在 mysql.gtid_executed表中。恢复期间,这些GTID从二进制日志文件添加到表中。例外的是重新启动服务器时未启用二进制日志记录。在这种情况下,服务器无法访问二进制日志文件以恢复GTID,因此无法启动复制。
启用二进制日志记录后,该 mysql.gtid_executed表将不保存所有已执行事务的GTID的完整记录。该信息由gtid_executed系统变量的全局值提供 。始终使用 @@GLOBAL.gtid_executed,它在每次提交后都会更新,以表示MySQL服务器的GTID状态,而不查询 mysql.gtid_executed表。
1.4 mysql.gtid_executed表压缩
随着时间的流逝, mysql.gtid_executed表中可能会出现很多行,表会越来越大,为了节省空间,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如果您这样做,应该准备增加表可能需要的磁盘空间量 。
该mysql.gtid_executed表的压缩由名为的专用前台线程执行
thread/sql/compress_gtid_table。该线程未在的输出中列出SHOW PROCESSLIST,但可以在threads表中的一行中查看 ,如下所示:
- mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
- RESOURCE_GROUP: SYS_default
- *************************** 2. row ***************************
- THREAD_ID: 45
- NAME: thread/sql/compress_gtid_table
- TYPE: FOREGROUND
- PROCESSLIST_ID: 6
- PROCESSLIST_USER: NULL
- PROCESSLIST_HOST: NULL
- PROCESSLIST_DB: NULL
- PROCESSLIST_COMMAND: Daemon
- PROCESSLIST_TIME: 8757
- PROCESSLIST_STATE: Suspending
- PROCESSLIST_INFO: NULL
- PARENT_THREAD_ID: 1
- ROLE: NULL
- INSTRUMENTED: YES
- HISTORY: YES
- CONNECTION_TYPE: NULL
- THREAD_OS_ID: 7602
- RESOURCE_GROUP: SYS_default
- 2 rows in set (0.01 sec)
二、GTID复制的优缺点
2.1 GTID优势
更简单的实现failover,不用以前那样在需要找log_file和log_pos。 2、更简单的搭建主从复制。 3、比传统的复制更加安全。 4、GTID是连续的没有空洞的,保证数据的一致性,零丢失。
2.2 GTID的限制
不允许在同一个事务内对事务表和非事务进行DML操作,例如在同一个事务内先update innodb表,然后update myisam表。因为GTID强制每一个GTID对应一个事务,而在同一个事务内既操作innodb表又操作myisam,就会产生两个GTID; 2、不允许CREATE TABLE … SELECT语句,首先这种语句对于statement格式的binlog是不安全的;而对于row格式的binlog,这种语句在binlog实际是分成两个event进行记录的,一个记录create创建操作,一个记录insert操作,那么就有可能这两个操作是对应到同一个GTID上,而当将这两个拥有相同GTID的event传到从库时,从库就会忽略拥有相同GTID的insert操作,造成数据丢失; 3、CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE不允许在事务内执行,只有在事务以外并且autocommit=1才能正常执行; 4、不支持sql_slave_skip_counter,如果需要跳过事务,可以用以下方法:
- set @@session.gtid_next='需要跳过的事务gtid'
- begin;commit;
- set session gtid_next=automatic;
【编辑推荐】