Gtid概念
从 MySQL 5.6.5 开始新增了一种基于 GTID 的复制方式。通过 GTID保证了每个在主库上提交的事务在集群中有一个唯一的ID。
这种方式强化了数据库的主备一致性,故障恢复以及容错能力。
在原来基于二进制日志的复制中,从库需要告知主库要从哪个偏移量进行增量同步,如果指定错误会造成数据的遗漏,从而造成数据的不一致。借助GTID,在发生主备切换的情况下,MySQL的其它从库可以自动在新主库上找到正确的复制位置,这大大简化了复杂复制拓扑下集群的维护,也减少了人为设置复制位置发生误操作的风险。另外,基于GTID的复制可以忽略已经执行过的事务,减少了数据发生不一致的风险。
GTID用来代替classic的复制方法,不在使用binlog+pos开启复制。而是使用master_auto_postion=1的方式自动匹配GTID断点进行复制。(更简单的实现failover,不用以前那样在需要找log_file和log_Pos)
什么是Gitd
GTID (Global Transaction ID) 是对于一个已提交事务的编号,并且是一个全局唯一的编号。 GTID 实际上 是由
UUID+TID 组成的
。其中 UUID 是一个 MySQL 实例的唯一标识。TID代表了该实例上已经提交的事务数量,并且随着事务提交单调递增。
下面是一个GTID的具体形式:3E11FA47-71CA-11E1-9E33-C80AA9429562:23,冒号分割前边为uuid,后边为TID。
GTID 集合可以包含来自多个 MySQL 实例的事务,它们之间用逗号分隔。
如果来自同一MySQL实例的事务序号有多个范围区间,各组范围之间用冒号分隔。例如: e6954592-8dba-11e6-af0e-fa163e1cf111:1-5:11-18,e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27 可以使用show master status实时查看当前事务执行数
在传统的slave端,binlog是不用开启的,但是在GTID中,slave端的binlog是必须开启的,目的是记录执行过的GTID(强制)。
Gtid组成部分
前面是server_uuid:后面是一个序列号,例如:
server_uuid:sequence number
7800a22c-95ae-11e4-983d-080027de205a:10
UUID:每个mysql实例的唯一ID,由于会传递到slave,所以也可以理解为源ID。
Sequence number:在每台MySQL服务器上都是从1开始自增长的序列,一个数值对应一个事务。
Gtid的作用
Gtid采用了新的复制协议旧协议是,首先从服务器上在一个特定的偏移量位置连接到主服务器上一个给定的二进制日志文件,然后主服务器再从给定的连接点开始发送所有的事件。
新协议有所不同,支持以全局统一事务ID (GTID)为基础的复制。当在主库上提交事务或者被从库应用时,可以定位和追踪每一个事务。GTID复制是全部以事务为基础,使得检查主从一致性变得非常简单。如果所有主库上提交的事务也同样提交到从库上,一致性就得到了保证。
Gtid的工作原理
①当一个事务在主库端执行并提交时,产生GTID,一同记录到binlog日志中。
②binlog传输到slave,并存储到slave的relaylog后,读取这个GTID的这个值设置gtid_next变量,即告诉Slave,下一个要执行的GTID值。(SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:7'/*!*/;)
③sql线程从relay log中获取GTID,然后对比slave端的binlog是否有该GTID。
④如果有记录,说明该GTID的事务已经执行,slave会忽略。
⑤如果没有记录,slave就会执行该GTID事务,并记录该GTID到自身的binlog,
在读取执行事务前会先检查其他session持有该GTID,确保不被重复执行。
⑥在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
要点
1、slave在接受master的binlog时,会校验master的GTID是否已经执行过(一个服务器只能执行一次)。
2、为了保证主从数据的一致性,多线程只能同时执行一个GTID。
浅析gtid
#主库上操作
mysql> insert into test values(1000);
mysql> commit;
mysql> show master status;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+------------------------------------------+
| master-bin.000001 | 2350 | | | 18f5da07-a096-11ea-8c70-000c290e1abf:1-9 |
+-------------------+----------+--------------+------------------+------------------------------------------+
[root@localhost ~]# mysqlbinlog /var/lib/mysql/master-bin.000001 | tail -100f
# at 2087
#200604 21:52:40 server id 1 end_log_pos 2152 CRC32 0x4ebee83f GTID last_committed=8 sequence_number=9 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:9'/*!*/;
# at 2152
#200604 21:52:40 server id 1 end_log_pos 2228 CRC32 0x34ada420 Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1591278760/*!*/;
BEGIN
/*!*/;
# at 2228
#200604 21:52:40 server id 1 end_log_pos 2279 CRC32 0x3ee3a3ff Table_map: `students`.`test` mapped to number 108
# at 2279
#200604 21:52:40 server id 1 end_log_pos 2319 CRC32 0xda7bff0e Write_rows: table id 108 flags: STMT_END_F
BINLOG '
qPzYXhMBAAAAMwAAAOcIAAAAAGwAAAAAAAEACHN0dWRlbnRzAAR0ZXN0AAEDAAH/o+M+
qPzYXh4BAAAAKAAAAA8JAAAAAGwAAAAAAAEAAgAB//7oAwAADv972g==
'/*!*/;
# at 2319
#200604 21:52:40 server id 1 end_log_pos 2350 CRC32 0xf7033eee Xid = 132
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
------------------------------------------------------------------------------------
#从库操作
#发现master主机插入的数据,已经在slave主机进行了同步,并且启用了log-bin-updates,在slave主机中的二进制日志记录了master主机操作。
[root@localhost ~]# mysqlbinlog /var/lib/mysql/master-bin.000002 | tail -100f
# at 654
#200604 21:52:40 server id 1 end_log_pos 719 CRC32 0xb6fd0db9 GTID last_committed=2 sequence_number=3 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:9'/*!*/;
# at 719
#200604 21:52:40 server id 1 end_log_pos 782 CRC32 0xc13ca7e7 Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1591278760/*!*/;
BEGIN
/*!*/;
# at 782
#200604 21:52:40 server id 1 end_log_pos 833 CRC32 0x6fb18d98 Table_map: `students`.`test` mapped to number 108
# at 833
#200604 21:52:40 server id 1 end_log_pos 873 CRC32 0xa78d775c Write_rows: table id 108 flags: STMT_END_F
BINLOG '
qPzYXhMBAAAAMwAAAEEDAAAAAGwAAAAAAAEACHN0dWRlbnRzAAR0ZXN0AAEDAAGYjbFv
qPzYXh4BAAAAKAAAAGkDAAAAAGwAAAAAAAEAAgAB//7oAwAAXHeNpw==
'/*!*/;
# at 873
#200604 21:52:40 server id 1 end_log_pos 904 CRC32 0x98a3302b Xid = 21
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
我们从上面输出可以清楚的看见事务id如下(根据上面提到的,这个是第9个事务):
last_committed=8 sequence_number=9 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:9'/*!*/;
GTID的简单工作流程如下:
(1)在master上产生一个事务并且提交,并且写入binlog
(2)master上的binlog发送到slave,slave接收完毕并且写入relay log,slave读取到这个GTID,并设置gtid_next的值,例如:
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= '18f5da07-a096-11ea-8c70-000c290e1abf:9'/*!*/;
然后告诉slave接下来的事务必须使用GTID,并写入到它自己的binlog里。
(3)slave检查并确认这个GTID没有被使用,如果没有被使用,那么开始执行这个事务并写入到它自己的binlog里。
(4)由于gtid_next的值不是空的,slave不会尝试去生成一个新的gtid,而是通过主从复制来获取GTID。