binlog的GTID模式基础及基于GITD的数据恢复

本文中的mysql版本:5.7.26

前言

gtid功能是mysql5.6版本开始新加入的特性,在5.7、8.0及以上版本中做了加强,特别是从mysql8.0版本开始,应该是主推基于gtid模式的复制了。通过gtid保证了每个在主库上提交的事务在集群中有一个唯一的ID。这种方式强化了数据库的主备一致性,故障恢复以及容错能力。虽说gtid是mysql5.6加入的特性,但是在5.6版本中并不支持该功能,也就是说mysql5.6版本中无法开启该功能。5.7版本中 即使没有开启gtid,默认也会以匿名的方式记录gtid,匿名的方式记录的gtid是由系统进行管理和维护的,我们是无法使用的。所以 如果要搭建mysql主从复制模式,并且是基于gtid模式进行复制的话,mysql版本最低是5.7的版本。


在传统的mysql基于二进制日志的模式复制中,从库需要告知主库 要从哪个二进制日志文件中的那个偏移量进行增量同步,如果指定错误会造成数据的遗漏,从而造成数据的不一致。借助gtid,在发生主备切换的情况下,mysql的其它从库可以自动在新主库上找到正确的复制位置,这大大简化了复杂复制拓扑下集群的维护,也减少了人为设置复制位置发生误操作的风险。另外,基于gtid的复制可以忽略已经执行过的事务,减少了数据发生不一致的风险。所以说,相比mysql传统的主从复制模式,gtid模式的复制对于 DBA/开发人员/运维 等相关技术人员更加友好。实际上 gtid这个功能 主要是为了主从复制而来的,gtid是为了代替mysql传统的主从复制模式(通过某一个binlog二进制日志文件中的pos位置偏移量等相关信息 来进行复制)。当然了,gtid在单机状态下 作用不是很大,它主要还是为了主从而来的。


可能大多数人第一次听到gtid的时候会感觉有些突兀,但是从架构设计的角度,gtid是一种很好的分布式ID实践方式,通常来说,分布式ID有两个基本要求:

    ①、全局唯一性

    ②、趋势递增

这个ID因为是全局唯一,所以在分布式环境中很容易识别,因为趋势递增,所以ID是具有相应的趋势规律,在必要的时候方便进行顺序提取。所以换一个角度来理解gtid,其实是一种优雅的分布式设计。

GTID的基本概念和组成(Global Transaction ID)

GTID (Global Transaction ID) 是对于一个已提交事务的编号,并且是一个全局唯一的编号。 GTID实际上是由server_uuid+transaction_id组成的。


server_uuid:是一个mysql实例的唯一标识,每一台mysql实例的server_uuid都是不同的,在mysql第一次启动时,会自动生成并持久化到auto.cnf文件(存放在mysql的数据目录下,这是一个非常重要的文件,不能删除,这一部分是不会变的),也可以在mysql中执行select @@server_uuid;命令查看当前mysql实例的server_uuid,显示的结果就是auto.cnf文件中保存的server_uuid。


transaction_id:代表了该实例上已经提交的事务数量,transaction_id是一个从1开始的自增计数,表示在这台mysql实例上执行的第n个事务。并且随着事务的增加,值一次递增(也就是 值依次+1)


注意:这里的事务和innodb中的事务是不一样的,innodb中的事务 指的是DML语句的操作,而gtid中的事务(transaction_id)指的是 binlog中的事件,如果不理解binlog中的事件,可以理解为gtid中的事务,包含了DDL、DML、DCL等语句。


小总结:gtid就是 事务的ID,唯一识别号,全局唯一。随着事务记录到binlog中,用来标识事务。每一个事务都有一个gtid_log_event(gtid的日志事件)。


下面是一些gtid的具体展现形式:

0a27af20-28d1-11ea-8a83-fa163e680ae8:1  // 表示 在以0a27af20-28d1-11ea-8a83-fa163e680ae8 为 唯一标识的mysql实例上执行的第1个数据库事务。


一组连续的事务可以用 - 连接的事务序号范围表示。例如:0a27af20-28d1-11ea-8a83-fa163e680ae8:1-5


gtid可以包含来自多个mysql实例的事务,它们之间用“,”(英文逗号)分隔。如果来自同一 mysql实例的事务序号有多个范围区间,各组范围之间用冒号分隔。例如:

0a27af20-28d1-11ea-8a83-fa163e680ae8:1-5:11-18,

0a27af20-28d1-11ea-8a83-fa163e680ae8:1-27

GTID的开启

主要是通过2个配置选项参数来控制gtid功能的开启,在mysql配置文件中的mysqld标签下增加以下2个配置,然后重启mysql即可开启gtid功能

gtid_mode=on -- 开启gtid功能
enforce_gtid_consistency=true -- 启动 强制gtid的一致性 如果开启gtid功能则此参数最好(必须)要开启。slave在做同步复制时,无须找到binlog日志和POS点,直接change master to master_auto_position=1即可,自动根据gtid进行同步数据。同时 强制要求只允许复制事务安全的事务,什么是安全的事务,自行搜索下即可。(建议开启)所以说gtid完全是为了主从数据一致性做的,单机的话gtid作用不是太大。


查看gtid是否开启

mysql>  select @@gtid_mode;
+-------------+
| @@gtid_mode |
+-------------+
| ON          |
+-------------+
1 row in set (0.00 sec)

ON表示开启,OFF表示未开启


开启gtid后,查看当前二进制日志文件的状态:

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

Executed_Gtid_Set 这一列非常关键,它表示:执行过的gtid的集合,开启gtid之后,这一列就会显示一共产生了多少gtid的信息了。


创建一个测试数据库,再次查看Executed_Gtid_Set这一列的值,就会发现这一列已经产生了gtid的信息了:

mysql> create database gtid_text; -- 这里使用的是DDL语句
Query OK, 1 row affected (0.01 sec)

mysql> show master status;
+------------------+----------+--------------+------------------+----------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                      |
+------------------+----------+--------------+------------------+----------------------------------------+
| mysql-bin.000001 |      328 |              |                  | 0a27af20-28d1-11ea-8a83-fa163e680ae8:1 |
+------------------+----------+--------------+------------------+----------------------------------------+
1 row in set (0.00 sec)

通过上图可以看到已经产生了gtid的信息了。


此时看一下,开启gtid之后,mysql在binlog日志文件中的事件这里是如何记录的:


mysql>  show binlog events in '你的binlog日志文件名称';


示例截图如下:

g1.png

上图中可以看到binlog中的一些具体信息,包括事件类型(Event_type)有的是gtid类型。有了gtid之后,就可以不用管pos和end_log_pos这2个列的值了,直接通过gtid就可以管理binlog日志,可以通过gtid截取日志、数据恢复等操作。


上图中 Info 这一列可以看到 SET @@SESSION.GTID_NEXT= '0a27af20-28d1-11ea-8a83-fa163e680ae8:1' 这一行信息, 其中 0a27af20-28d1-11ea-8a83-fa163e680ae8 就是前面提到过的server_uuid,而 1 就是transaction_id(事务id)。

注意:上面使用的是DDL语句,在binlog日志文件中,gtid是这样记录的。


使用DML语句,然后观察下 在binlog中,DML语句的操作,gtid是如何记录的?

DML语句,在gtid中的记录 是 如下图中的记录形式的:

g2.png

如上图所示,DML的语句,gtid是以 begin开始、commit结束 作为一个gtid的记录。


小总结:每一个DDL语句都是一个gtid记录,而DML语句 是以begin开始、commit结束 为一个gtid记录。这个就是gtid的记录方式,要搞清楚。

基于GTID进行日志截取

基于gtid进行日志截取也是使用mysqlbinlog工具进行截取,一般都是配合以下三个参数进行配合使用。

    ①、--include-gtids   // 截取指定的gtid

    ②、--exclude-gtids  // 排除指定的gtid

    ③、--skip-gtids        // 跳过gtid的幂等性机制的检查,也可以理解为,截取日志的时候 不带有gtid的信息


截取示例:

# 注意:以下示例 都是在binlog日志文件 所在的目录中执行的

# 示例1:截取多个日志文件的多个范围的gtid记录。(示例就直接来个多范围的,这样单文件或单个gtid截取就更不在话下了)
/usr/local/mysql/bin/mysqlbinlog --include-gtids '0a27af20-28d1-11ea-8a83-fa163e680ae8:5-11' mysql-bin.000002 mysql-bin.000003 >/tmp/gtid.sql # 截取5-11号的gtid记录,从 mysql-bin.000002 mysql-bin.000003 这2个日志文件进行截取,将截取的结果保存到/tmp/gtid.sql文件中。注意:5-11号gtid必须在mysql-bin.000002 mysql-bin.000003 这2个日志文件中存在(有记录)


# 示例2:截取指定数据库的gtid 就比上面的示例1 多了一个-d选项
/usr/local/mysql/bin/mysqlbinlog -d xxx --include-gtids '0a27af20-28d1-11ea-8a83-fa163e680ae8:5-11' mysql-bin.000002 mysql-bin.000003 >/tmp/gtid2.sql # xxx 替换为 你要截取的数据库名称

GTID的幂等性

幂等性:用户对同一操作,发起一次或多次请求的数据结果是一致的,不会因为多次请求而导致最终的数据结果不一致的问题。(前提是:在具备幂等性机制的这种情况下)。简单来说 就是 不重复操作。


mysql开启gtid之后,binlog就具备了幂等性的机制了。


如果在基于gtid进行数据恢复的时候,你在截取gtid记录时,没有加--skip-gtids这个选项的话,那你在mysql使用source命令导入sql文件 进行数据恢复的话,会发现 数据并没有恢复成功。就是因为gtid集合中已经有了这些gtid记录,而基于幂等性的机制,已经有的gtid是不会进行操作的,既然不会进行操作,那自然数据恢复就不会成功了。


可以观察下,gtid日志截取的时候,截取2个 然后分别保存这2个文件,其中一个截取的时候--skip-gtids选项另一个截取的时候不加--skip-gtids选项,然后分别打开这2个文件,仔细观察2个文件中的内容就会发现,加了--skip-gtids选项的文件里面 没有gtid的信息(文件里面没有 SET @@SESSION.GTID_NEXT='xxxxxx' 没有这行gtid记录的信息),所以在数据恢复的时候,会跳过gtid的幂等性机制检查,从而数据恢复成功。


小总结:开启gtid后,mysql在恢复binlog时,重复的gtid的事务(gtid记录)就不会在执行了。所以说,如果是基于gtid进行数据恢复的时候,一定要注意gtid的幂等性这个机制。因为 一不小心就恢复失败。

基于GTID的数据恢复

配合上面的gtid日志截取,完事后,在mysql里面使用source命令进行导入sql文件即可。不要忘了 截取gtid日志记录的时候,加上--skip-gtids这个选项。好吧,举个栗子吧:

# 注意:以下示例 都是在binlog日志文件 所在的目录中执行的

# 截取要恢复数据的gtid记录,并保存到一个sql文件中
/usr/local/mysql/bin/mysqlbinlog --skip-gtids --include-gtids '0a27af20-28d1-11ea-8a83-fa163e680ae8:5-11' mysql-bin.000002 mysql-bin.000003 >/tmp/gtid.sql


然后,在mysql中使用source命令进行数据恢复:

mysql> source /tmp/gtid.sql

小总结:基于gtid数据恢复的时候,一定要仔细查看到底要截取那些gtid记录进行数据恢复,避免截取gtid记录的时候 把那些drop、delete等操作的gtid也给截取出来了,导致数据恢复的时候,又相当于删除了。。

尾声

结束 睡觉 妈的 又是一个午夜23:35



而我是他们口中的传奇,不败立于这天地    ---->李宇春【咏春】



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复12人评论7,777人参与