写点什么

GTID 详解

作者:乌龟哥哥
  • 2022 年 6 月 17 日
  • 本文字数:4584 字

    阅读完需:约 15 分钟

前言

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 日志文件名称';


示例截图如下:



上图中可以看到 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 中的记录 是 如下图中的记录形式的:



如上图所示,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 也给截取出来了,导致数据恢复的时候,又相当于删除了。。

发布于: 刚刚阅读数: 6
用户头像

乌龟哥哥

关注

正在努力寻找offer的大四小菜鸟 2021.03.16 加入

擅长 Hbuilder、VS Code、MyEclipse、AppServ、PS 等软件的安装与卸载 精通 Html、CSS、JavaScript、jQuery、Java 等单词的拼写 熟悉 Windows、Linux、 等系统的开关机 看–时间过得多快,不说了,去搬砖了

评论

发布
暂无评论
GTID详解_6月月更_乌龟哥哥_InfoQ写作社区