MySQL 5.6 的新特性之一,是加入了全局事务 ID (GTID) 来强化数据库的主备一致性,故障恢复,以及容错能力。但是,有关 GTID 的作用和原理,在 MySQL 官方网站 的文档库中却很少被提到。
随着 MySQL 5.6 的 rc 版本号原来越高(这意味着 MySQL 5.6 向正式发布越来越近),想要全面了解这个神秘功能的需求也越来越急迫。因此,在这篇博客里,我打算从有限的文档出发,通过分析 MySQL 源码,逐步了解 MySQL GTID 和它在主备复制中的作用。
什么是 GTID?
有关全局事务 ID(GTID),容易找到的是这一篇文档:
http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html
在这篇文档里,我们可以知道全局事务 ID 的官方定义是:
GTID = source_id:transaction_id
在 MySQL 5.6 中,每一个 GTID 代表一个数据库事务。在上面的定义中,source_id 表示执行事务的主库 uuid(server_uuid),transaction_id 是一个从 1 开始的自增计数,表示在这个主库上执行的第 n 个事务。MySQL 会保证事务与 GTID 之间的 1 : 1 映射。
例如,下面就是一个 GTID:
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
表示在以 3E11FA47-71CA-11E1-9E33-C80AA9429562 为唯一标示的 MySQL 实例上执行的第 23 个数据库事务。
很容易理解,MySQL 只要保证每台数据库的 server_uuid 全局唯一,以及每台数据库生成的 transaction_id 自身唯一,就能保证 GTID 的全局唯一性。
什么又是 server_uuid?
MySQL 5.6 用 128 位的 server_uuid 代替了原本的 32 位 server_id 的大部分功能。原因很简单,server_id 依赖于 my.cnf 的手工配置,有可能产生冲突 —— 而自动产生 128 位 uuid 的算法可以保证所有的 MySQL uuid 都不会冲突。
在首次启动时 MySQL 会调用 generate_server_uuid() 自动生成一个 server_uuid,并且保存到 auto.cnf 文件 —— 这个文件目前存在的唯一目的就是保存 server_uuid。
在 MySQL 再次启动时会读取 auto.cnf 文件,继续使用上次生成的 server_uuid。
使用 SHOW 命令可以查看 MySQL 实例当前使用的 server_uuid:
SHOW GLOBAL VARIABLES LIKE 'server_uuid';
它是一个 MySQL 5.6 global variables,文档链接在这里: server_uuid
全局唯一的 server_uuid 可以解决由 server_id 配置冲突带来的 MySQL 主备复制的异常终止(BUG #33815):
在 MySQL 5.6,Slave 向 Master 申请 binlog 时,会首先发送自己的 server_uuid,Master 用 Slave 发送的 server_uuid 代替 server_id (MySQL 5.6 之前的方式)作为 kill_zombie_dump_threads 的参数,终止冲突或者僵死的 BINLOG_DUMP 线程。
关于 GTID 的更多细节:
在 MySQL 5.6 源码内部,GTID 的数据结构可以用伪码写成:
(代码路径:mysql-5.6.9-rc\sql\rpl_gtid.h, 754 line)
Gtid := (sidno, gno)
其中 sidno 是代表 sid 的 32-bit 序号,sid 是 source_id 或者 server_uuid 的二进制表示 —— 这里我先忽略 sidno 和 sid 的关联(这需要解释一些另外复杂的东西)。gno 是 64 位整数,等同于上面提到的 transaction_id。
在 MySQL 内部更常见的数据类型是 Gtid_set,表示一组 GTID 的集合,在官方文档中通常写成 GTIDs。例如,下面就是一个 GTIDs:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
表示在 MySQL 实例 3E11FA47-71CA-11E1-9E33-C80AA9429562 上执行的第 1 到第 5 个数据库事务。
复杂一点的是:如果这组 GTIDs 来自不同的 source_id,各组 source_id 之间用逗号分隔;如果事务序号有多个范围区间,各组范围之间用冒号分隔,例如:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5:11-18,
2C256447-3F0D-431B-9A12-575BB20C1507:1-27
GDITs 和 Gtid_set
在 MySQL 5.6 源码里,Gtid_set 是个较为复杂的数据结构,基本结构可以用伪码表示成:(代码路径:mysql-5.6.9-rc\sql\rpl_gtid.h, 842 line)
Gtid_set := array( sidno => link_list(Interval) )
Interval := (start, end)
用示意图表示应该是这样:
Gtid_set 的结构是一个以 sidno 为序号的数组(同样的,这里允许我先忽略什么是 sidno 这个挠头的问题),每个数组元素都指向一条 Interval 组成的链表,链表中的每个 Interval 用来存放一组事务 ID(gno)的区间,例如 (1,5)。
假设上文中 uuid:3E11FA47-71CA-11E1-9E33-C80AA9429562 对应的 sidno 为 1,那么下面的 GTIDs:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
可以用 Gtid_set 表示为:
Gtid_set
| sidno: 1 | -> Interval(1, 5)
MySQL 5.6 引入 Gtid_set 数据结构的目的是为了轻量级的记录大量的连续全局事务 ID,比如已经在 Slave 上执行的全局事务 ID, 或者主库上正在运行的全局事务 ID。这些集合往往包含海量的全局事务 ID。
当 transaction_id / gno 保持连续,Gtid_set 可以在常数时间内判断一个 GTID 是否包含在集合内。这很容易用来检查一个 GTID 是否已经执行过,因此可以用在防止 MySQL 事务在同一个 Slave 上重复执行这类场景。
Gtid_set 也支持一些常见的集合操作,比如检查另一个 Gtid_set 是否子集:is_subset,比如两组 Gtid_set 做交集:intersection。这些方法在根据 GTIDs 自动查找主备复制位点,进行自适应主备切换时有用到。
有个值得一提的细节是,Gtid_set 为了减少链表导致的内存碎片,所有的 Interval 对象都是用 chunk 方式分配的,chunk 大小为 8 * sizeof(Interval)。
打断,解释一下 sidno
为减少在索引里保存 128 位 server_uuid 的消耗,Gtid_set 使用 int32 类型的 sidno 代替 server_uuid 作为 Interval 链表的索引。因此,MySQL 需要另一个数据结构在 128 位的 server_uuid 与 32 位的 sidno 之间建立映射,这个结构叫 Sid_map:
(代码路径:mysql-5.6.9-rc\sql\rpl_gtid.h, 467 line)
Sid_map := hash_map(sid => sidno)
它基本上就是一个 server_uuid 到 sidno 的 hash_map, 并且负责顺序产生 sidno:第一个放入 Sid_map 的 sidno 为 1,第二个 sidno 为 2 ...... 注意,生成的 sidno 是临时性的,在 Sid_map 被释放或者 MySQL 实例重启后又会重新分配。
在创建 Gtid_set 时,MySQL 会调用 ensure_sidno() 方法保证 array 内有足够的空间容纳 Sid_map 中所有分配的 sidno。
因为 Sid_map 是一个读多写少的数据结构(显然,只有 MySQL 集群增加或者更换实例时,server_uuid 才会增加),MySQL 用一个读写锁来保护 Sid_map,每当 Gtid_set 在查 Sid_map 时加读锁;每当 Gtid_set 找到新的 server_uuid 需要分配 sidno 时,加写锁。
基本上,MySQL 5.6 所有的 server_uuid 都通过一个全局变量 global_sid_map 来映射。相应的,也有一个全局锁 global_sid_lock 在保护这个 Sid_map。这些代码在 mysqld.cc 的 gtid_server_init 方法里可以找到。
(代码路径:mysql-5.6.9-rc\sql\mysqld.cc, 1719 line)
最后介绍一下 Gtid_state
现在,MySQL 5.6 有了记录全局事务 ID 的数据结构 Gtid_set,又维持了一个全局 Sid_map 来映射 server_uuid 与 sidno,下面我们可以开始接触 MySQL 全局事务 ID 的核心数据 Gtid_state 了:
Gtid_state := (logged_gtids, lost_gtids, owned_gtids)
全局事务状态 Gtid_state 在 MySQL 5.6 内只有唯一一个实例,目的是存储三组全局事务 ID 的集合,每个集合的功能我会在下一篇博客阐述:
+---------------+-------------------------------------------------------------+
| 名称 | 功能 |
+---------------+-------------------------------------------------------------+
| logged_gtids | 写入到 binlog 的全局事务 ID 集合。 |
+---------------+-------------------------------------------------------------+
| lost_gtids | 已经从 binlog 删除的全局事务 ID 集合。 |
+---------------+-------------------------------------------------------------+
| owned_gtids | 正在执行的全局事务 ID 与 MySQL 线程 ID 的集合 |
+---------------+-------------------------------------------------------------+
注:owned_gtids 变量的类型是 Owned_gtids, 它基本上可以看作一个 Gtid (sidno, gno) 到 owner_thread_id 的 hash_map:
Owned_gtids := array(sidno => hash_map(Node))
Node := (gno, owner_thread_id)
其中 gno 是 Gtid 中的事务 ID。
(未完待续)
相关推荐
官方文档:http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html在这篇文档里,我们可以知道GTID(全局事务 ID) 的官方定义是:GTID实际上是由UUID+TID组成的,其中UUID是一个MySQL实例的唯一标识,TID...
MySQL 5.6 的新特性之一,是加入了全局事务 ID (GTID) 来强化数据库的主备一致性,故障恢复,以及容错能力。 什么是GTID? 官方文档:http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html在这篇文档里,...
基于GTID(全局事务标示符)。 需要注意的是:GTID方式不支持临时表!所以如果你的业务系统要用到临时表的话就不要考虑这种方式了,至少目前最新版本MySQL5.6.12的GTID复制还是不支持临时表的。 所以本教程主要是...
mysql5.6版本linux安装包 添加或更改功能 复制: 新变量 simplified_binlog_gtid_recovery 可用于更改在恢复期间搜索以前GTID的二进制日志文件的方式,从而在存在大量二进制日志文件时加快进程。(Bug#69097,Bug...
MySQL数据库很多的高可用及scale out方案都是基于replication实现的,相对其他方案整体性价比会高出很多。 从MySQL 5.6.2新增GTID特性,利用GTID以让我们更加容易的管理MySQL复制,并有效提高数据库一致性。 这次...
GTID(Global Transaction ID)是对于一个已提交事务的编号,并且是一个全局唯一的编号。下文给大家介绍MySQL 5.6 GTID新特性实践,感兴趣的朋友一起看看吧
GTID (global transaction identifier) 即全局事务ID, 保证了在每个在主库上提交的事务在集群中有一个唯一的ID. 在原来基于日志的复制中, 从库需要告知主库要从哪个偏移量进行增量同步, 如果指定错误会造成数据的...
1.概述 从MYSQL5.6 开始,mysql开始支持GTID复制。... GTID即全局事务ID,器保证为每一个在主上提交的事务在复制集群中可以生成一个的ID. GTID=source_id:transaction_id source_id:是主库的ser
MYSQL 5.7 MHA(GTID+ROW)部署 Mysql 数据库GDIT主从复制
MySQL 5.7 GTID MTS CrashSafeMySQL 5.7 GTID MTS CrashSafe 并发半同步复制并发半同步复制项目要求:3.Cr
搭建虚拟机centos6.0, mysql5.6.10主从复制,死活不同步,搞了一整天找到这篇文章终于OK了,特分享一下,需要的朋友可以参考下
《深入理解MySQL主从原理32讲》专栏包含GTID部分、Event部分、主库部分、从库部分四大块来详细讲解主从原理。希望能帮助读者朋友们解决关于主从同步中的一些疑问。八怪写作风格很是严谨,几乎每篇都是从源码入手去...
MySQL主从复制原理 _ 异步复制 _ 半同步复制 _ GTID复制
本篇文章主要介绍了MySQL主从复制实战 - 基于GTID的复制,基于GTID的复制是MySQL 5.6后新增的复制方式.有兴趣的可以了解一下。
的并行 MTS多级主从多级主从Crash safe半同步架构半同步架构项目要求:3.Crash safe master&slave双主 mastera&mast
mysql_56_GTID_in_a_nutshell
│ │ 2_MySQL传统复制手动切换和GTID复制原理及切换.mp4 │ │ 3_Mycat原理和schema配置讲解.mp4 │ │ 4_Mycat schema配置讲解.mp4 │ │ 5_Mycat企业高可用配置.mp4 │ │ 作业.docx │ │ │ └─MySQL DBA ...
下面小编就为大家带来一篇Mysql GTID Mha配置方法。小编觉的挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧