面试题数据库

CPU狂飙900%,如何分析,怎么定位,怎么溯源处理

2023-03-27  本文已影响0人  前浪浪奔浪流

CPU飙升200% 以上是生产容易发生的场景

场景:1:MySQL进程飙升900%

评估:

大家在使用MySQL过程,想必都有遇到过CPU突然过高,或者达到200%以上的情况。

数据库执行查询或数据修改操作时,系统需要消耗大量的CPU资源维护从存储系统、内存数据中的一致性。

并发量大并且大量SQL性能低的情况下,比如字段是没有建立索引,则会导致快速CPU飙升,如果还开启了慢日志记录,会导致性能更加恶化。生产上有MYSQL 飙升900% 的恶劣情况。

定位:

处理:

show processlist;

发现类似很多相同的SQL语句,一直处于query状态中。

select id form user where user_code = 'xxxxx';

初步分析可能是 user_code 字段没有索引导致。接着查询user表的索引情况:

show index form user;

发现这个字段是没有建立索引。增加索引之后,该条SQL查询能够正常执行。
3、没隔一会,又发生大量的请求超时问题。接着进行分析,发现是开启了 慢日志查询。大量的SQL查询语句超过慢日志设置的阀值,于是将慢日志关闭之后,速度瞬间提升。CPU的使用率基本保持在300%左右。但还不是理想状态。

4、紧接着将部分实时查询数据的SQL语句,都通过缓存(redis)读写实现。观察一段时间后,基本维持在了70%~80%。

总结:其实本次事故的解决很简单,就是添加索引与缓存结合使用。

1)不推荐在这种CPU使用过高的情况下进行慢日志的开启。因为大量的请求,如果真是慢日志问题会发生日志磁盘写入,性能贼低。
2)直接通过MySQL show processlist命令查看,基本能清晰的定位出部分查询问题严重的SQL语句,在针对该SQL语句进行分析。一般可能就是索引、锁、查询大量字段、大表等问题导致。
3)再则一定要使用缓存系统,降低对MySQL的查询频次。
4)对于内存调优,也是一种解决方案。

场景2:Java进程飙升900%

一般来说Java 进程不做大量 CPU 运算,正常情况下,CPU 应该在 100~200% 之间,

但是,一旦高并发场景,要么走到了死循环,要么就是在做大量的 GC, 容易出现这种 CPU 飙升的情况,CPU飙升900%,是完全有可能的。

定位: CPU飙升问题定位的一般步骤是:

1、首先通过top指令查看当前占用CPU较高的进程PID;
2、查看当前进程消耗资源的线程PID:top -Hp PID
3、通过print命令将线程PID转为16进制,根据该16进制值去打印的堆栈日志内查询,查看该线程所驻留的方法位置。
4、通过jstack命令,查看栈信息,定位到线程对应的具体代码。
5、分析代码解决问题。

处理:

1、如果是空循环,或者空自旋。

处理方式:可以使用Thread.sleep或者加锁,让线程适当的阻塞。

2、在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等。

处理方式:可以减少对象的创建数量,或者,可以考虑使用 对象池。

3、其他的一些造成CPU飙升的场景,比如 selector空轮训导致CPU飙升 。

处理方式:参考Netty源码,无效的事件查询到了一定的次数,进行 selector 重建。

采用top命令定位进程

登录服务器,执行top命令,查看CPU占用情况,找到进程的pid

top
image.png

很容易发现,PID为29706的java进程的CPU飙升到700%多,且一直降不下来,很显然出现了问题。

使用top -Hp命令定位线程

使用 top -Hp命令(为Java进程的id号)查看该Java进程内所有线程的资源占用情况(按shft+p按照cpu占用进行排序,按shift+m按照内存占用进行排序)

此处按照cpu排序:

top -Hp 23602
image.png

多个线程的CPU占用达到了90%多。我们挑选线程号为30309的线程继续分析。

使用jstack命令定位代码

1.线程号转换5为16进制
printf “%x\n” 命令(tid指线程的id号)将以上10进制的线程号转换为16进制:

printf  "%x\n"  30309
image.png

转换后的结果分别为7665,由于导出的线程快照中线程的nid是16进制的,而16进制以0x开头,所以对应的16进制的线程号nid为0x7665

2.采用jstack命令导出线程快照

通过使用dk自带命令jstack获取该java进程的线程快照并输入到文件中:

jstack -l 进程ID > ./jstack_result.txt 

命令(为Java进程的id号)来获取线程快照结果并输入到指定文件。

jstack -l 29706 > ./jstack_result.txt

3.根据线程号定位具体代码

在jstack_result.txt 文件中根据线程好nid搜索对应的线程描述

cat jstack_result.txt |grep -A 100  7665
image.png

根据搜索结果,判断应该是ImageConverter.run()方法中的代码出现问题
当然,这里也可以直接采用

jstack <pid> |grep -A 200 <nid>

来定位具体代码

$jstack 44529 |grep -A 200 ae24
"System Clock" #28 daemon prio=5 os_prio=0 tid=0x00007efc19e8e800 nid=0xae24 waiting on condition [0x00007efbe0d91000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrentC.TimeUnit.sleep(TimeUnit.java:386)
    at com.*.order.Controller.OrderController.detail(OrderController.java:37)  //业务代码阻塞点

分析代码解决问题
重启项目后,测试发现项目运行稳定,对应项目进程的CPU消耗占比不到10%。


image.png
上一篇下一篇

猜你喜欢

热点阅读