MySQL运维系列 之 CPU瓶颈故障案例分析
Updated:
很久没发布文章了,并不是春哥不爱学习了,而是最近我们碰到了一些瓶颈没有解决,心有不甘的苦苦探索着
现在容我给大家分析一起新鲜出炉热乎乎滚烫烫的故障案例
在分析案例前,据我们故障报告得到的数据分析显示,最近2年出现的故障
80%来自硬件,20%来自压力瓶颈(因为瓶颈分析和解除瓶颈做的及时)
压力瓶颈中:80%来自disk(IO压力、磁盘空间)、20%来自CPU
今天主要聊聊cpu相关的问题
一、背景
- 环境
|
|
- 案发经过
凌晨做了一次数据库升级,读写从old master 切到了 new master,一切顺利
然后,到早高峰的时候,接到告警,紧张刺激的一天开始了
告警内容如下:
- slow query 上涨至 10000/s , 平时 5/s
- thread running 上涨至 169/s , 平时 10/s
- too many connection 频繁
- cpu 使用率上涨至 99% , 平时 20-30%
- 服务器响应变慢
二、故障降级
根据以上报警信息,第一时间查看slow query
- 没有发现特殊的慢查询(扫描行数多、执行特别慢等),其余基本都是在100ms ~ 500ms
- java每次读写的时候,有带有大量的 select @@session.tx_read_only。
- 正常情况下,这样的查询是不会慢的,但是为了先解决链接数多的问题,我们让业务调整了链接参数:useLocalSessionState=true
可是剧情并没有像我们想的那样发展,虽然有所好转(TMC从持续告警降低到间歇性告警),也就是问题并没有彻底解决
接下来,我们能做的就是用DCT(数据库配置管理系统)将master上的查询分流一部分(60%)到slave上来
好消息是: Master 压力降低,slow 降低,各种指标恢复正常
坏消息是: Slave 压力瞬间就起来了,然后表现的症状跟之前的Master一模一样
再接下来,我们就继续分流,再分60%的量到另外一台slave
最终,Master和slave 系统各指标稳定了下来
好了,现在我们可以有时间来回顾和分析下刚刚发生的一连串事件
- 案件名称:cpu暴走事件
cpu的sys 使用率非常高
- 线索1: 数据库升级
- 线索2: 高峰期流量大
- 线索3: 数据没有预热
临时控制:流量迁移
后续方案:
继续追踪,找到根本原因
1.1 保留现场,寻找蛛丝马迹
1.2 模拟案发,重现问题回退版本
数据库降级方案:并不太好做,可能存在的问题更多,不到万不得已,我们不愿意这样尝试。
三、寻找真相
- 当cpu飙高的时候,究竟发生了什么
使用perf top 去看看哪些函数调用非常多,结果又遇到了另外一个坑,机器直接hang住,不久就挂了
直接上图
对呀,正所谓祸不单行就是这么个道理哈
所以,接下来,我们的解题思路变成了这样
- 解决perf 导致linux挂掉的情况
- 解决cpu高的瓶颈
2.1 线下场景还原: 保留了案发当时的slow sql , 使用mysqlslap模拟并发sql,将压力打在测试库上
2.2 线上场景还原: 使用tcpcopy,在线引流将真实压力打在测试库上
四、测试
4.1 测试用例一
先解决perf的问题,因为有了它,才可能知道坑在哪里
- 模拟问题,跑perf的时候让linux挂掉的场景复现
|
|
- perf为什么会挂
google了一圈后,发现一丢丢可操作的点,大致的意思是说: perf在centos 2.6.32的内核中有bug
链接如下:
|
|
于是,我们也没想要去看是什么因素导致perf在linux 2.6.32 中触发了bug,我们所幸直接将系统升级到 centos 7来看看问题是否可以重现
再次经过上述的测试发现:perf + MySQL replicate 没问题了,现在基本断定是perf + kernel 2.6.32 遇到相关bug了
不过,既然centos 7上的perf可以用,那么我们所幸在centos 7上复现 cpu高的问题
4.2 线下场景还原: 保留了案发当时的slow sql , 使用mysqlslap模拟并发sql,将压力打在测试库上
- 单一 slow sql 测试 + 多条 slow sql 混合测试
SQL语句:
|
|
结果cpu压力果然上涨了,且sys cpu非常高,跟我们的故障场景类似
SQL序号 | dstat结果 |
---|---|
1. | usr:18 sys:3 idle:78 |
2. | usr:8 sys:87 idle:5 |
4. | usr:8 sys:86 idle:6 |
n. | … … |
20. | usr:7 sys:88 idle:5 |
然后用perf top -g 追踪统计下发生了啥
看到的确是内核态的函数比较多啊,但是这些是干啥的,并不知道哇,只知道自旋锁是瓶颈哇
如果这个没法统计,那我只能用pstack去碰碰运气,追踪下当时压力大的时候的点发生了什么?
|
|
似乎发现了什么,没错,就是时区转换函数,从google得之,在有timestamp的场景下,如果MySQL的time_zone=system,会调用linux的时区函数,从而内核态的cpu使用率高
timestamp的场景包括很多,什么情况下会导致sys cpu飙高呢,我们不妨测试一把:
得到的最后结论是:
- sys cpu高,只跟结果集包含timestamp有关系,跟查询条件是否有timestamp无关
- 使用time_zone=’+8:00’ 来替换 time_zone=’system’ ,可以大大降低sys cpu的使用率
最后,我们再针对一开始的21种语句使用 time_zone=’+8:00’ 来模拟一遍
SQL序号 | time_zone=system | time_zone=’+8:00’ |
---|---|---|
1. | usr:18 sys:3 idle:78 | usr:19 sys:3 idle:77 |
2. | usr:8 sys:87 idle:5 | usr:92 sys:1 idle:6 |
4. | usr:8 sys:86 idle:6 | usr:93 sys:2 idle:4 |
n. | … … | |
20. | usr:7 sys:88 idle:5 | usr:90 sys:2 idle:7 |
很明显,问题得到解决
4.3 线下场景还原:使用TCPCOPY的方式在线引入真实的流量测试
- 简单介绍下tcpcopy
在搭建和测试tcpcopy的时候也遇到一些坑,其中在路由上卡壳了很久,数据包一直被reset, 最终排查下来 1. 由于腾讯机房的交换机上设置了arp自动应答 2. test serve和assistant server必须在同一网段
反正过程蛮曲折的,这不是我们今天的重点,先忽略。
详细的tcpcopy文章,请参考同事整理的 : tcpcopy介绍
- 继续问题追踪
好了,根据4.1的测试,我们认为问题是时区转换导致的,但是为了严谨,我们在对生产环境的真实引流做一次测试,如果引流的时候出问题,然后设置时区可以解决问题,才能正在确定问题的所在
好消息是: 当qps达到早高峰的量时,cpu sys压力高的场景复现了
坏消息是: 设置了time_zone=’+8:00’ 之后,并没有得到好转,还是自旋锁的问题
pstack的跟踪并没有Time_zone_system::gmt_sec_to_TIME(st_mysql_time* , 所以跟时区转换没有多大关系
|
|
继续从分析结果中寻找猫腻,以上很多函数都见过,那么我们就去寻找下平常不起眼的一些的函数名一个个排查。
经过排查后,脑海中对alloc,malloc,my_raw_mallo 有点敏感,意识中就突然想到了tcmalloc,而tcmalloc我的印象中就是解决内存溢出和高并发场景的
然后,死马当活马医,抱着试一试的心跳来安装测试下
4.4 tcmalloc 安装和部署
|
|
4.4 malloc vs tcmalloc
malloc
tcmolloc
以上测试经过了10次以上,通过以上测试看:
- 使用glibc自带的malloc,在同样的sql模型下,当MySQL QPS 达到16000的时候,CPU使用率已经高达92%,而sys cpu明显比较高
- 使用google的tcmalloc,在同样的sql模型下,当MySQL QPS达到21000的时候,CPU使用率才39%,而sys cpu明显比较低
至此,告一段落,接下来我们还有一系列要做的事情
总结
SQL模型 决定了 QPS的极限 , 我们需要一套相对靠谱的模型来探测机器的极限, 给后期扩容给出相对靠谱的理论依据
简单的SQL模型意味着更高的QPS,SQL越简单越好,复杂的业务逻辑可以放在程序端处理
time_zone = ‘+8:00’ 来代替 time_zone=’system’ , 用以获得更好的性能
[ ] 如果发现perf top 中,mysqld libc-2.12.so [.] __tz_convert ,那么这个设置会带来极好的性能优化
tcmalloc 来替换 malloc , 用以获得更好的并发,tcmalloc 在小内存分配管理上更加优秀
透明大页需要关闭 : 在之前的N次测试中,perf偶尔会看到compact_zone的出现,而这个可能跟透明大页相关,此问题我没能复现出来,但仍引起我们的关心
如何快速、高效的做读迁移
perf不要随便乱用,尽量升级Linux的内核到高版本,来降低系统bug的可能,当然也许会引进新的bug —需测试
如何用tcpcopy将master的读压力引流到test server , 毕竟master的操作需要非常谨慎