前文?MySQL 5.6 全局事务 ID(GTID)实现原理(一)? 介绍了 MySQL 5.6 全局事务 ID 的定义和相关的数据结构 Gtid_set 与 Sid_map。接下来,这一篇的主要目标是深入了解文章最后提到的全局事务状态 Gtid_state。并且,如果可能 —— 顺便介绍下这些 Gtid_state 在主备复制中的功能:
?
全局事务状态 Gtid_state
?
Gtid_state 是 MySQL 5.6 内的一个全局对象,它的数据结构如下:?
(mysql-5.6.9-rc\sql\rpl_gtid.h,line 2043)
?
Gtid_state := (logged_gtids, lost_gtids, owned_gtids)
?
创建 Gtid_state 的代码可以在?mysqld.cc 的 gtid_server_init 方法里找到。
(代码路径:mysql-5.6.9-rc\sql\mysqld.cc, 1719 line)
?
Gtid_state 的重要性是,它维护三个与全局事务 ID 有关的 MySQL global variables:
?
logged_gtids / `gtid_executed`
?
存储已经执行过,并且记录到 binlog 的全局事务 ID 集合。它对应的 MySQL variable 是?gtid_executed,可以用命令:
?
SHOW GLOBAL VARIABLES LIKE 'gtid_executed'
?
查看数据库上已经执行的全局事务 ID。
?
MySQL 5.6 在开启?--gtid_mode?=ON 后,每当执行完一个事务,就会调用 Gtid_state 的 update_on_flush() 方法,把事务对应的 GTID 写入 logged_gtids。
?
详细一点的过程是这样的:
?
为了防止写入 binlog 的是一组不完整的事务,MySQL 会缓存整个事务的 binlog 内容在 binlog_cache_data 中。如果事务提交,MySQL 会执行一个叫 ordered_commit() 的三阶段提交操作:
(源代码:mysql-5.6.9-rc\sql\binlog.cc,line 6245)
?
第一步:调用?process_flush_stage_queue() 和 flush_io_cache() 将缓存在 binlog_cache_data 中的内容刷出到 binlog 文件。此时,Gtid_state 的 update_on_flush() 调用到,事务对应的 GTID 写入 logged_gtids。
?
第二步:调用 sync_binlog_file() 在 binlog 文件上执行 fsync() 保证内容更新到磁盘。?
?
第三步:调用?process_commit_stage_queue() 执行所有事务提交,在存储引擎上调用 ha_commit_low()。
?
结束后,MySQL 调用 Gtid_state 的 update_on_commit(),从 owned_gtids 里删除完成 ?commit 的全局事务 ID。
?
与名字相同,logged_gtids 用来判断 MySQL 有没有执行某个事务。例如,在 Master 发送 binlog 时,MySQL 5.6 能够根据 Slave 提供的 logged_gtids 记录,自动过滤 binlog 中不需要执行的事务(请参考新增 COM_BINLOG_DUMP_GTID 协议)。?
?
另外,MySQL 5.6 支持在 START SLAVE 时指定?UNTIL_SQL_BEFORE_GTIDS / UNTIL_SQL_AFTER_GTIDS 条件,让 Slave 执行完所需要的事务后就自动停止复制。这个功能也依赖于 Slave 的 logged_gtids 记录来检查作为条件的 GTIDs 是否满足。
?
lost_gtids / `gtid_purged`
?
记录从 binlog 删除的全局事务 ID 集合。它对应的 MySQL Global variable 是:gtid_purged? 。
?