Mysql 5.7 Gtid内部学习(九) 实际案例(一)

本案例是一个朋友的案例他也写了出来如下:
https://mp.weixin.qq.com/s/XSnFkuYzIlGWMaXIl-oPeQ
但是和他交流后他也准备改因为分析有一些小问题。


一、触发条件

  • binlog_gtid_simple_recovery=false。
  • 5.7.6以上版本。
  • Gtid 关闭或者Gtid中途开启有大量的未开启Gtid的binlog。

二、本案例回顾

  • 版本:MySQL版本 5.7.19。
  • 故障为:大概每半小时发生一次故障,整个Mysql压力巨大,很多简单的操作都相应缓慢。使用iotop,top等工具都发现Mysql某个线程有大量的I/O。
  • 分析方法:使用strace发现有大量的binlog文件读取。
  • binlog_gtid_simple_recovery=false。
  • Gtid关闭,中途开启,但是留下了很多未开启Gtid的binlog。
  • 数据库没有重启,但是由于expire_logs_days触发了binlog删除。

三、故障分析

其实本案例就是前文第七部分总结中的:

Gtid关闭,simple_recovery=flase
5.7.6以上:这种方式一定得到正确的Gtid集合
重启Mysql不扫秒全部的binlog,如果是中途打开GTID重启任然需要扫描多个binlog因为需要找到Gtid event。
purge binlog或者超过参数expire_logs_days参数设置不触发全binlog扫描,如果是中途打开GTID重启任然需要扫描多个binlog因为需要找到Gtid event。

从案例中我们得知是中途开启的Gtid,但是留下了很多未开启Gtid的binlog,从第六部分源码bool MYSQL_BIN_LOG::init_gtid_sets()函数的分析,我们知道删除binlog后也会触发正向查找来获取gtid_purged(Gtid_state.lost_gtids)。当读取到第一个binlog的时候虽然获取到了PREVIOUS GTID EVENT但是没有GTID EVENT,而simple_recovery=flase所以需要继续查找下一个文件,直到找到同时包含PREVIOUS GTID EVENT和GTID EVENT的 那个binlog才会停止,那么显然这种情况下那些Gtid关闭的时候生成的binlog将会全部扫描一遍,如果量大那么代价将是巨大的。
而案例中每半个小时会触发一次binlog切换,因为触发超过expire_logs_days参数设置导致binlog进行删除,触发了大量的binlog扫描。
显然有了前面的基础这个案例很容易分析。

四、案例模拟

这个案例非常好模拟。我打算直接使用strace查看。因为不是每位朋友都能方便使用GDB调试。
使用测试版本社区版本5.7.17:

+---------------+-----------+
| Log_name      | File_size |
+---------------+-----------+
| binlog.000027 |       198 |
| binlog.000028 |       198 |
| binlog.000029 |       198 |
| binlog.000030 |       198 |
| binlog.000031 |       198 |
| binlog.000032 |       198 |
| binlog.000033 |       198 |
| binlog.000034 |       198 |
| binlog.000035 |       198 |
| binlog.000036 |       198 |
| binlog.000037 |       198 |
| binlog.000038 |       198 |
| binlog.000039 |       198 |
| binlog.000040 |       198 |
| binlog.000041 |       198 |
| binlog.000042 |       198 |
| binlog.000043 |       154 |
+---------------+-----------+

mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name                    | Value     |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery      | OFF       |
| enforce_gtid_consistency         | ON        |
| gtid_executed_compression_period | 1000      |
| gtid_mode                        | OFF       |
| gtid_next                        | AUTOMATIC |
| gtid_owned                       |           |
| gtid_purged                      |           |
| session_track_gtids              | OFF       |
+----------------------------------+-----------+
8 rows in set (0.02 sec)
mysql> show variables like '%expir%';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| disconnect_on_expired_password | ON    |
| expire_logs_days               | 1     |
+--------------------------------+-------+
2 rows in set (0.06 sec)

然后我修改了系统时间同时Mysql开启Gtid

[root@test1 ~]# date -s '2017-12-13 10:10:10'
Wed Dec 13 10:10:10 CST 2017
mysql> set global gtid_mode=1;
Query OK, 0 rows affected (0.02 sec)

mysql> set global gtid_mode=2;
Query OK, 0 rows affected (0.01 sec)

mysql> set global gtid_mode=3;
Query OK, 0 rows affected (0.02 sec)

mysql> show variables like '%gtid_mode%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_mode     | ON    |
+---------------+-------+
1 row in set (0.02 sec)

到一步我们已经达到了触发的标准,只要手动触发一次flush binary logs,让binlog刷新就会看到。当然线上是binlog满了做的切换。
这个时候开始做strace,并且做flush tables,我们观察到

mysql> flush binary logs;
Query OK, 0 rows affected (0.30 sec)

strace:

[pid  6551] 10:17:15.936738 read(62, "/mysql/mysql5.7.17/binlog.000027"..., 528) = 528 <0.000039>
[pid  6551] 10:17:15.936834 stat("/mysql/mysql5.7.17/binlog.000027", {st_mode=S_IFREG|0640, st_size=198, ...}) = 0 <0.000025>
[pid  6551] 10:17:15.936925 lseek(3, 0, SEEK_SET) = 0 <0.000017>
[pid  6551] 10:17:15.936983 read(3, "/mysql/mysql5.7.17/binlog.000043"..., 165) = 165 <0.000018>
[pid  6551] 10:17:15.937076 lstat("/mysql", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000020>
[pid  6551] 10:17:15.937144 lstat("/mysql/mysql5.7.17", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000586>
[pid  6551] 10:17:15.937819 unlink("/mysql/mysql5.7.17/binlog.000027") = 0 <0.000109>
[pid  6551] 10:17:15.938009 stat("/mysql/mysql5.7.17/binlog.000028", {st_mode=S_IFREG|0640, st_size=198, ...}) = 0 <0.000021>
[pid  6551] 10:17:15.938119 lstat("/mysql", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000020>
[pid  6551] 10:17:15.938228 lstat("/mysql/mysql5.7.17", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000021>
[pid  6551] 10:17:15.938314 unlink("/mysql/mysql5.7.17/binlog.000028") = 0 <0.000073>
.....
[pid  6551] 10:17:15.954677 lstat("/mysql/mysql5.7.17", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000019>
[pid  6551] 10:17:15.954756 unlink("/mysql/mysql5.7.17/binlog.000041") = 0 <0.000099>
[pid  6551] 10:17:15.954920 stat("/mysql/mysql5.7.17/binlog.000042", {st_mode=S_IFREG|0640, st_size=198, ...}) = 0 <0.000021>
[pid  6551] 10:17:15.955022 lstat("/mysql", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000018>
[pid  6551] 10:17:15.955087 lstat("/mysql/mysql5.7.17", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000018>
[pid  6551] 10:17:15.955159 unlink("/mysql/mysql5.7.17/binlog.000042") = 0 <0.000130>

受限篇幅我这里删除了一些。我们看到很多read/lseek系统调用正是读取binlog的证据。
至此整个案例模拟完成。

五、总结

前文已经描述过在5.7.6以上binlog_gtid_simple_recovery应该设置为true,这样可以避免可能的大量的binlog的扫描。具体分析可以参考第七节和从第六部分源码bool MYSQL_BIN_LOG::init_gtid_sets()函数的分析。

时间: 2024-09-23 08:14:32

Mysql 5.7 Gtid内部学习(九) 实际案例(一)的相关文章

Mysql 5.7 Gtid内部学习(十) 实际案例(二)

本案例是我真实遇到过的一个坑,也在前文中不止一次的提到,当时也是非常纳闷,其实知道原因后只能说为什么会这么坑. 一.触发条件 本案列我测试过4个版本 percona Mysql 5.7.14 官方社区 Mysql 5.7.17 percona Mysql 5.7.19 percona Mysql 5.7.15 其中percona Mysql 5.7.14和官方社区 Mysql 5.7.17有这个问题.其他版本未知 已知percona Mysql 5.7.14或者官方社区 Mysql 5.7.17

Mysql 5.7 Gtid内部学习(一) 导读

Mysql Gtid特性是5.6加入的一个强大的特性,它的目的在于使用Gtid的Mysql能够在整个复制环境中能够自动的切换,而不像以前需要指定文件和位置,这也一定是未来发展的方向,我们熟知的MGR也是基于Gtid的,所以了解Gtid的原理也是必要的. Gtid的维护是完全自动的,但是实际使用上确实有较多的坑,也导致很多朋友对Gtid还是觉得畏惧,本系列文章将从Gtid模块的源码出发分析,并且给出总结,然后结合运维和案例进行综合的解析,我希望抛砖引玉让希望了解源码的朋友也有所收获,但是能力有限特

Mysql 5.7 Gtid内部学习(二) Gtid相关内部数据结构

1. Gtid基本格式 单个Gtid: e859a28b-b66d-11e7-8371-000c291f347d:1 前一部分是server_uuid,后面一部分是执行事务的唯一标志,通常是自增的.内部使用Gtid这种数据结构表示,后面会描述. 区间Gtid: e859a28b-b66d-11e7-8371-000c291f347d:1-5 前一部分是server_uuid,后面一部分是执行事务的唯一标志集合,在内部使用Gtid_set中某个Sidno对应的Interval节点表示,后面会描述.

Mysql 5.7 Gtid内部学习(五) mysql.gtid_executed表/gtid_executed变量/gtid_purged变量的更改时机

本节将集中讨论下面三种Gtid更新的时机,这部分相当重要,后面的故障案列会和这节有关.下面先来看一下他们的定义 mysql.gtid_executed表:Gtid持久化的介质,Mysql启动阶段会读取这个表来获取gtid_executed变量的值. gtid_executed变量(show global variables):Mysql数据库已经执行了哪些Gtid事务,处于内存中.show slave status中的Executed_Gtid_Set也取自这里. gtid_purged变量(s

Mysql 5.7 Gtid内部学习(八) Gtid带来的运维改变

依托前文的解析来讲5.7中 Gtid带来的运维改变,我想理解应该是更加深刻,这节主要讨论以下几个部分: 如何跳过一个事务 mysqldump导出行为的改变 5.7中搭建基于Gtid的主从 5.7中Gtid的主从的切换 5.7中在线改变Gtid模式 一.如何跳过一个事务 和传统基于位置的主从不同,如果从库报错我们需要获得从库执行的最后一个事务,方法有如下: show slave status \G 中的 Executed_Gtid_Set. show global variables like '

Mysql 5.7 Gtid内部学习(四) mysql.gtid_executed表Previous gtid Event的改变

之所以把mysql.gtid_executed表的作用和Previous gtid Event的改变放到一起进行描述是因为它们后面文章探讨的基础.这部分使用到了我自己使用C语言写的原生binlog解析工具infobin. 百度云盘下载如下:http://pan.baidu.com/s/1jHIWUN0 一.Gtid event 为什么要先描述什么是Gtid event呢?因为后面会用到,实际上在中其核心元素就是一个形如: 31704d8a-da74-11e7-b6bf-52540a7d243:1

Mysql 5.7 Gtid内部学习(三) Gtid和Last_commt/sequnce_number的生成时机

一.Gtid生成类型 这里首先使用源码的解释给出三种类型: AUTOMATIC_GROUP GTID_GROUP ANONYMOUS_GROUP 其中AUTOMATIC_GROUP通常用于主库开启Gtid的情况,GTID_GROUP通常用于备库和使用了GTID_NEXT的情况下. 源码中有详细解释如下: /** Specifies that the GTID has not been generated yet; it will be generated on commit. It will d

[MySQL 5.6] GTID内部实现、运维变化及存在的bug

由于之前没太多深入关注gtid,这里给自己补补课,本文是我看文档和代码的整理记录. 本文的主要目的是记下跟gtid相关的backtrace,用于以后的问题排查.另外也会讨论目前在MySQL5.6.11版本中存在的bug. 本文讨论的内容包括 一.主库上的gtid产生及记录 二.备库如何使用GTID复制 三.主备运维的变化 四.MySQL5.6.11存在的bug 前言:什么是GTID 什么是GTID呢, 简而言之,就是全局事务ID(global transaction identifier ),最

MySQL EXPLAIN命令详解学习(执行计划)

MySQL EXPLAIN命令详解学习(执行计划) MySQL EXPLAIN 命令详解 MySQL的EXPLAIN命令用于SQL语句的查询执行计划(QEP).这条命令的输出结果能够让我们了解MySQL 优化器是如何执行 SQL 语句的.这条命令并没有提供任何调整建议,但它能够提供重要的信息帮助你做出调优决策. 1 语法 MySQL 的EXPLAIN 语法可以运行在SELECT 语句或者特定表上.如果作用在表上,那么此命令等同于DESC 表命令.UPDATE 和DELETE 命令也需要进行性能改