概述
从MySQL 5.7.5开始, GTID存储在mysql数据库的gtid_executed表中。表中的一行代表一个单独的GTID或一个GTID集合,包含三个字段:原始服务器的UUID、开始和结束的事务ID;对于代表一个单独GTID的行,开始和结束的事务ID是相同的。
在MySQL服务器安装或者升级的时候,如果mysql.gtid_executed表不存在就会被创建;创建mysql.gtid_executed表的语句类似下面这样:
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表只有在gtid_mode为ON或ON_PERMISSIVE的时候才存储GTIDs。在这个前提下,不管binlog是否开启,GTIDs都会存储在mysql.gtid_executed表中。但是,binlog是否开启(log_bin为ON或者OFF)会影响mysql.gtid_executed表存储GTIDs的格式。
注意:
RESET MASTER操作会清空mysql.gtid_executed表.
binlog禁用
如果binlog被禁用(log_bin是OFF),MySQL在存储每一个事务的同时存储这个事务对应的GTID。此外,如果binlog被禁用,mysql.gtid_executed表按照用户配置的频率来压缩,详情见下面的数据压缩。
binlog开启
如果binlog是开启的(log_bin为ON),除了将每一个事务的GTID存储在mysql.gtid_executed表中,当一个binlog切换或者MySQL服务器关闭时,MySQL会把此前所有binlog文件中所包含的GTIDs写到新的binlog文件中。写入的binlog事件为PREVIOUS_GTIDS_LOG_EVENT,PREVIOUS_GTIDS_LOG_EVENT事件的物理格式如下,具体定义在libbinlogevents/include/control_events.h中:
一个单独的GTID事件(Gtid_event)的物理格式如下,具体定义在libbinlogevents/include/control_events.h中:
如果MySQL异常关闭,此前的binlog文件所包含的GTID集合就没有机会写入到mysql.gtid_executed表中。这种情况下,在恢复阶段这些GTIDs会被存储在mysql.gtid_executed表和gtid_executed系统变量中。
数据压缩
随着时间的推移,mysql.gtid_executed表可能会存储了很多行,每一行对应相同Server上的不同GTID,每一个GTID的事务ID是顺序的,类似下面这样:
mysql> SELECT * FROM mysql.gtid_executed;
复制
如果上面由多行组成的GTID集合,被周期性地(压缩)合并成跨越整个区间的单独一行,那么存储空间将会大大减少,如下所示:
当GTID开启时,mysql.gtid_executed表会被周期性地压缩,可以配置executed_gtids_compression_period变量来控制压缩的频率;默认是1000,即每1000个事务后进行一次GTIDs的压缩。设置为0表示禁止压缩,当然这需要更多的磁盘空间来存储mysql.gtid_executed表的数据。具体的代码实现在rpl_gtid_persist.cc文件的Gtid_table_persistor::compress_first_consecutive_range函数中。
注意:
当binlog开启的时候,executed_gtids_compression_period设置是无效的,mysql.gtid_executed表只有在binlog文件切换的时候才进行压缩.
mysql.gtid_executed表的压缩是通过一个单独线程来进行的,这个线程在GTID开启的时候创建。这个线程不会在show processlist中输出,但是可以通过线程表来查看:
mysql> SELECT * FROM PERFORMANCE_SCHEMA.THREADS WHERE NAME LIKE '%gtid%'\G
复制
注意:
在binlog禁用的情况下将executed_gtids_compression_period设置为0,compress_gtid_table线程将一直Sleep。
后记
内容主要翻译至MySQL官网,加入了一些对照源码的注解,主要目的是了解GTID的存储行为。
Facebook MySQL分支引入了RocksDB作为存储引擎,并且专门针对GTID落存储引擎表的环节做了优化,Github上记录性能提升明显;未来可以研究InnoDB引擎是否可以进行类似优化。
MySQL的GTID引用UUID来保证了严谨的唯一性,但是UUID存储(内存/磁盘)和运算(转换)的开销也不小;对比之下,MariaDB的Domain Id要轻量很多。