目录

一.故障现象... 1

二.初步分析... 2

三.排障过程... 2

1.排查是否QPS或insert并发请求上升导致问题发生... 2

2.排查是否锁资源等待或block导致了insert变慢... 3

3.排查是否表上无用索引导致的写入时间较长... 5

4、人工抓取perf,排查CPU上升期间的资源消耗... 5

5、疑似触发MySQL BUG,进一步分析... 6

四.优化过程... 8

1.初步优化方案... 8

2.删除一批无用索引,将服务器内存升级到80G.. 9

3.未达预期,还需继续优化... 11

4.热表索引分析... 11

5.随机GUID建立索引的性能测试... 13

6.热表索引优化方案... 14

7.前缀索引的性能测试... 14

8.删除热表上非顺序的二级索引... 16

五.最终优化方案... 17

六.总结... 18

 

一.故障现象

有台生产服务器间歇性CPU飙升,出现大量insert语句的慢查询,相关业务的响应时间随之大幅上升

二.初步分析

从监控报告来看,这台服务器的负载并不高

消耗时间高的SQL是insert系列语句

三.排障过程

1.排查是否QPS或insert并发请求上升导致问题发生

排查并发请求没并有突然升高,反而在问题时间段先大幅下降再小幅上升,这个现象说明MySQL在问题时间段的处理能力发生了下降

表的insert并发频率并没有大的波动

2.排查是否锁资源等待或block导致了insert变慢

以一句慢查询insert into为例,查询SQL执行的明细记录,这句SQL的执行时间在异常时间点达到12秒,对应locktime只有63微秒,排除了表锁等待,排查问题发生过程中,rowlock相关指标没有大幅上升,排除rowlock等待,也没有明显的block产生,正常执行时<3毫秒。

3.排查是否表上无用索引导致的写入时间较长

我们都知道表上大量的无用索引不仅浪费存储空间,也会增加数据写入的成本,因此在测试环境新建了相同的表,保留索引不变,测试索引维护成本的消耗

看到这句insert into正常执行时的各阶段的消耗,总体执行时间不到2ms

4、人工抓取perf,排查CPU上升期间的资源消耗

参考命令如下,

注意:下面命令在生产上执行时有较低概率会导致服务器hang死

#生成mysql进程10秒内资源消耗采样报告

sudo perf record -p `pidof mysqld` -g -o /tmp/perf.data sleep 10

#查看报告

sudo perf report -i /tmp/perf.data

CPU资源消耗占比较高的是ibuf_get_volume_buffered_count_func函数,它主要有2个功能,一是统计change buffer中对于同一page ,buffer了多少空间,二是在准备插入类型为IBUF_OP_DELETE的操作缓存时,会预估在apply完该page上所有的ibuf entry后还剩下多少记录。

5、疑似触发MySQL BUG,进一步分析

通过网上搜索,了解到有相关的BUG

该BUG的链接:https://bugs.mysql.com/bug.php?id=77827

下面是BUG描述

MySQL对每个表对象独立分配rw lock,当开启change buffer时,Innodb会频繁的创建dummy table(一种用于线程私有的简单的索引结构),这种dummy index事实上无需使用states_latch,因为他是线程私有的;但mysql没有做区分,在创建rw lock时,会加全局锁rw_lock_list_mutex来维护全局读写锁链表rw_lock_list。

也看到Ali关于这个问题的分析,同时也发现几个关联的BUG,直到MySQL5.7.6版本,问题才完全修复

四.优化过程

1.初步优化方案

1、 从以上分析来看,这个问题的产生与业务大量的二级索引频繁更新是有关系的,目前DB下共有5094个索引。因此我们决定先删除大量无用的索引,看效果是否明显

2、还有个导致问题的原因是内存远小于数据集,计划将innodb_buffer_pool_size从64G扩充到70G-90G之间

2.删除一批无用索引,将服务器内存升级到80G

将这台服务器的内存升到80G后,对比了这个集群昨天和今天的运行情况。

总的来看,增加内存后,CPU波动有所缓解,CPU的峰值和高消耗持续的时间有所降低,运行时的并发线程数也有降低,但问题并没有根本解决。

这台服务器(QPS约4000)的change buffer使用情况,缓存最大时达到23G(说明有大量的二级索引在写入时需要加入缓存进行合并),但是按照目前80G的buffer pool,change buffer的最大值只有20G,仍有瓶颈

对比另外写入频繁的(QPS约9000)一台服务器change buffer的使用情况,最大只有256K

3.未达预期,还需继续优化

评估下来后,需要继续删除热表的索引,下面对热表的索引情况做了进一步分析

4.热表索引分析

分析DB下热表的时候发现每次CPU飙升,逻辑IO消耗时间较长的表排名TOP 2都是固定的两张表,约占了所有表逻辑IO消耗时间的80%。

这两张表有什么特征会影响到逻辑IO的消耗时间,下面其中一张表为例

表结构中有一个row_key的字段,和开发确认,这个字段存储的是随机的GUID值,对应这个字段上建立了一个索引idx_row_key。

我们知道Innodb的聚集索引和二级索引都是一颗B+树,row_key字段建立索引,在插入数据维护索引时,以row_key值的大小做为页和记录的排序规则,随着大量并发随机GUID值的插入,

为了保持B+树的平衡,新插入的数据可能会带来大量的页拆分的操作,这时change buffer起到了关键的优化作用,将二级索引的操作缓存下来,并进行操作合并,减少二级索引的随机IO。

这两个表的容量使用情况,记录数达到3亿条,二级索引占用了较多的容量

5.随机GUID建立索引的性能测试

在测试环境中模拟类似的场景,使用sysbench并发256线程进行压测

结论:

1、 随着表的记录数的增多,当达到千万级以上的记录数时,随机GUID字段上的二级索引维护开销很明显,对插入性能的影响逐渐增大(从开始的30K的QPS下降到约3K)。

2、 删除GUID字段的二级索引后,QPS处理能力大幅上升,恢复到40K的QPS

6.热表索引优化方案

1、 从索引使用统计来看这张表上的idx_row_key索引实际并没有使用过,如果能直接删除,优化效果预计会比较明显

2、 如果业务逻辑上row_key的索引确实需要,折中的办法可以尝试创建前缀索引

对随机GUID值的前8个字符创建索引,这样只在B+树中存储字符串的前几个字符的编码,能节约一部分空间,减少字符串的比较时间,在一定程度上缓解排序和页拆分的问题,语法如下:

ALTER TABLE table1 ADD INDEX idx_row_key_prefix(row_key(8));

3、 业务上修改逻辑,将完全随机的GUID生成规则改为顺序的GUID生成规则

7.前缀索引的性能测试

下面测试表的数据约9000W,建立了两个随机GUID字段的前11个字符的前缀索引,QPS稳定在约10K左右。

8.删除热表上非顺序的二级索引

观察一天下来,CPU高消耗的问题基本消除。

当前change buffer的使用有较大幅度的减少,与删除索引前相比降低了约74%

五.最终优化方案

将两张大表改造为以时间字段为分区函数的分区表,分区表只保留最近30天的数据,改造完成后,从change buffer的使用来看,已经降低到16K

六.总结

对于记录数多的大表,表上如果存在随机的GUID字段或非顺序的字符串字段,如果这些类型上建立二级索引,对于频繁的增删改操作,会带来较高的维护成本。

当change buffer使用频繁,空间很大时,服务器性能也会出现大幅下降。这时我们可以通过删除热表的二级索引,改造分区表,清理大表数据,OPTIMIZE TABLE等操作来进行优化。

【原创】记一次MySQL大表高并发写入引发CPU飙升的排障过程的更多相关文章

  1. 优秀后端架构师必会知识:史上最全MySQL大表优化方案总结

    本文原作者“ manong”,原创发表于segmentfault,原文链接:segmentfault.com/a/1190000006158186 1.引言   MySQL作为开源技术的代表作之一,是 ...

  2. MySQL 大表优化方案(长文)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  3. 从云数据迁移服务看MySQL大表抽取模式

    摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来. 小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨.一开始爆内存被客户怼,再后来迁移效率低下再被怼.MySQL ...

  4. [记录]一则清理MySQL大表以释放磁盘空间的案例

    一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...

  5. MySQL InnoDB 实现高并发原理

    MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...

  6. php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结)

    php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结) 一.总结 从外到内解决网站大流量高并发问题---从提交一个url开始(从用户按下搜索栏回车键开始) url最开始会到d ...

  7. 详解MySQL大表优化方案( 转)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  8. MySQL 大表优化方案探讨

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  9. mysql大数据高并发处理

    一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...

随机推荐

  1. HIIT训练第一波,值得收藏的训练计划

    下面这三套训练,收藏好,平时在家或者出差都能用! 即使你是一个健身新手,也并不意味着高强度间歇训练(HIIT)不适合你. 这种快节奏的训练已经显露出短时间内燃烧成吨卡路里的能力.所以,你并不需要再健身 ...

  2. 【Linux】SecureCRT连接Linux乱码

    SecureCRT连接linux出现乱码问题.解决方法. 打开SecureCRT-->option-->Session option

  3. java学习第01天(程序开发体验)

    1.基本写法 class Demo{ public static void main(String[] args){ System.out.print("Hello World") ...

  4. 训练赛第二场G题 ZOJ 2343

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2343 解题报告:首先我假设最后的正确的结果是a[1] , a[2 ...

  5. Python练习-递归二分算法

    # 编辑者:闫龙 #递归,二分算法演示 l = [i for i in range(1,100)]#定义一个列表l,并追加1-99的所有数字 def FindNum(num,l):#定义函数FindN ...

  6. Ubuntu 问题汇总

    1..bashrc环境变量失效,ls.cp等命令不能使用了: export PATH=/usr/bin:/bin 2.添加环境变量 echo '********' >> ~/.bashrc ...

  7. python+selenium初学者常见问题处理

    要做web自动化,第一件事情就是搭建自动化测试环境,那就没法避免的要用到selenium了. 那在搭建环境和使用过程中经常会遇到以下几类问题: 1.引入selenium包失败: 出现这种错误,一般分为 ...

  8. Spring4笔记3--Bean的装配

    Bean的装配: Bean 的装配,即 Bean 对象的创建.容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配. 1. 创建Bean对象的方式: 1. 通过 getBe ...

  9. AopProxyUtils.getSingletonTarget(Ljava/lang/Object;)Ljava/lang/Object;大坑

    这个问题太坑了,试了好多个版本,都是依赖冲突导致的, https://blog.csdn.net/qq_15003505/article/details/78430595 最后找到这一篇博客解决了,就 ...

  10. Shell编写8点建议

    这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训.事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条.毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生 ...