生产数据库服务器CPU使用率居高不下问题排查

Posted road2master

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生产数据库服务器CPU使用率居高不下问题排查相关的知识,希望对你有一定的参考价值。

现象与恢复

  Prometheus监控报警数据库服务器CPU一直维持在100%以上,没有丝毫下降。

  重启消费MQ消息的服务,以及MySQL实例后,CPU使用率断崖式下降,系统访问正常了。

原因分析

  第一时间,判断是慢查询所致。查看阿里云SQL分析工具,总扫描行数这个指标有异常,达到了亿级。并发现大量的相同的SQL一直在执行,

  最终定位于JPA的findAll()方法,参数是持久层对象PO,由于只使用了PO的一个属性(ID)作为查询条件【其实这里正确的使用应该是findAllById(),而非findAll()方法,因为条件只有一个并且是ID】,又由于封装findAll()的方法没判空,刚好查询出来的数据ID都为空,自动生成的SQL如下:

  select * from table_name;

  生成的SQL不带where子句,很简单粗暴查全表。导致业务逻辑的下一条SQL


  SELECT * FROM table_name WHERE id IN (id1, id2, id3, ...);

  根据全表数据的ID筛选过多的数据,致命的是这段逻辑是消费Canal发到Kafka的消息,canal监听的是核心服务的主表,数据量大,还做了分库分表,平均每天增量数据能达到 5~7k 条,所以这也是CPU使用率居高不下的重要原因。

复盘总结

  1. 复用其他同学的代码也务必把逻辑都仔细过一遍;

  2. 方法参数一定要校验,特别是非空判断,DAO层的方法更加要谨慎仔细;

  3. ORM框架尽量使用Mybatis,JPA没问题,问题出现在使用者不能熟悉掌握,这次出问题的findAll()方法换成findAllById()的话就不会出现这种情况了;

 

利用阿里开源工具进行排查线上CPU居高问题 转

出处: 利用阿里开源工具进行排查线上CPU居高问题

 

前言

  在我们开发过程中,无法避免的会出现所谓的垃圾代码,导致服务器的CPU一直处于100%。但我们应用已经上线,导致服务器CPU居高,但又不知道哪边出现的问题,我们应该怎么去找出哪边的代码出现问题呢?今天老顾就介绍几种工具去快速定位。

演示代码

我们先来编写一下代码,新建springboot的maven项目,创建web服务,引入SpringBoot内置web容器,pom.xml关键引用jar包如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

创建service:TestWhile

       技术图片

 

创建Controller:TestWhile

            技术图片

 

配置文件application.properties

server.port=80
server.servlet.context-path=/api

打包项目,上传测试服务器

java -jar demo-0.0.1-SNAPSHOT.jar &

打开浏览器,访问死循环方法

打开浏览器,地址栏输入http://xxxx/api/user/testWhile?size=2

返回“Hello 程序执行完毕”,说明调用成功。(开启了2个死循环)

到此问题代码,已经在服务器上面跑了。我们发现服务器报警,于是去线上排查。

原生方法

此方法无需额外安装工具,在没法连接互联网的情况下使用此方法排查效果较好。

topprintf都是Linux原生命令,jstackjstatjdk自带命令工具。

很多功能强大的Linux和java诊断工具也是以topjstackjstat为基础命令做的封装。

注意:jstack、jstat等命令需要jdk完整安装,linux自带的openJdk一般无此工具,可以在java的bin目录下查看是否有这些命令。

找到最耗CPU的进程

命令:top –c,显示进程运行信息列表

实例:top -c。

交互1:按1,数字1,显示多核CPU信息。

交互2:键入P (大写p),进程按照CPU使用率排序

技术图片

 

技术图片

 

我们看出了双核CPU使用率已经达到100%。

而第一个进程PID是373的就是我们要找的罪魁祸首了;可以看到进程最后一列,COMMAND注释的进程名:“java -jar demo-0.0.1-SNAPSHOT.jar”。

找到最耗CPU的线程

命令:top -H -p 【PID】,显示一个进程的线程运行信息列表

实例:top -Hp 373 ,如下图所示,可以看到多个高耗CPU使用率的线程

技术图片

 

转换线程PID为16进制

命令:printf “%x ” 【线程pid】,转换多个线程数字为十六进制,第4步使用时前面加0x。

实例:printf ‘%x ‘ 406 405 375 376,得到结果196、195、177、178;如下图所示:

技术图片

 

查看堆栈,定位线程

命令:jstack 【进程PID】| grep 【线程转换后十六进制】-A10 , 使用jstack获取进程PID堆栈,利用grep定位线程id,打印后续10行信息。

实例:jstack 373 | grep ‘0x196‘ -A10,如下图所示:

技术图片

 

我们通过查看堆栈信息,发现了问题是TestWhile.whileTrue引起的;

而且发现有2个GC线程,看上图中的“GC task thread#0 (ParallelGC)”,代表垃圾回收线程,该线程会负责进行垃圾回收

存储堆栈,批量查看

查看堆栈信息,我们也可以换个方法查看,可以先将jstack堆栈信息存储起来。

命令:jstack 【进程PID】> 【文件】

实例:jstack 373 > demo.dump,存储373进程的堆栈信息。

再使用cat + grep查找看看后面几个高CPU线程的堆栈信息。

实例:cat -n demo.dump | grep -A10 ‘0x196‘,如下图所示:

技术图片

 

可以看到线程0x196【线程196】产生堆栈信息,直指方法whileTrue。

GC查看

我们看到上图中有4个线程中的2个线程没有看到java代码,而是GC task thread#0 (ParallelGC),这个是GC垃圾回收的线程,是不是死循环导致了GC太频繁,导致CPU使用率居高不下呢?

我们使用jstat看下jvm的GC信息看看。

命令:jstat -gcutil 【进程PID】【毫秒】【打印次数】

实例:jstat -gcutil 373 2000 5,查看373进程的GC信息,每2秒打印一次,共打印5次,如下图所示:

技术图片

 

S0:幸存1区当前使用比例

S1:幸存2区当前使用比例

E:伊甸园区使用比例

O:老年代使用比例

M:元数据区使用比例

CCS:压缩使用比例

YGC:年轻代垃圾回收次数

FGC:老年代垃圾回收次数

FGCT:老年代垃圾回收消耗时间

GCT:垃圾回收消耗总时间

上面的原生方法查找要遵循一定的步骤,相对有些麻烦点,有没有比较简单的方法呢?往下看

Arthas(阿尔萨斯)

  Arthas(阿尔萨斯)是阿里巴巴开源出来的一个针对 java 的线上诊断工具,功能非常强大。Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。我们来看看

下载Arthas

使用arthas-boot(推荐)

下载arthas-boot.jar,然后用java -jar的方式启动:

curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar

 

技术图片

 

按1进入java进程,此时java进程PID已经变成373

进入阿尔萨斯完成,如下图,可以看到登录路径已经变成了[arthas@17376]$,可以输入dashboard,进入监控页面了。

监控查看

已经进入Arthas操作界面,输入dashboard,回车后将看到线程及堆栈信息,如图所示,arthas已经将cpu高使用率的线程给安排上了。

技术图片

 

上面我们就看到有2个线程居高不下,还有GC的数量和耗时。

thread【ID】查看线程

ctrl + c 退出dashboard界面,输入thread 32查看线程信息,如下图所示:

技术图片

 

可以看到是TestWhile类中的whileTrue方法中的put方法导致cpu使用率升高。

问题一下子就出来了,Arthas功能不单单止于此,可以直接反编译,看看代码。

jad反编译

使用Arthas自带的反编译方法jad,输入命令:

jad com.rainbow.demo.service.TestWhile

可以反编译java的class查看问题函数的具体代码,如下图所示:

技术图片

 

退出arthas

最后,既然问题已经找到,那就退出Arthas吧。输入命令:quit

Arthas的功能是非常强大的,这里就简单介绍,下一次老顾用专门的文章介绍

老顾在介绍一个更简单的一个脚本,立刻发现问题所在

show-busy-java-threads

show-busy-java-threads.sh这个工具是useful-scripts工具集的其中一个工具。

show-busy-java-threads用于快速排查Java的CPU性能问题(top us值过高),自动查出运行的Java进程中消耗CPU多的线程,并打印出其线程栈,从而确定导致性能问题的方法调用。

注意:此工具的核心还是使用jdk的jstack方法,只是在其上做了封装展示。

# 下载到当前目录下

下载地址:https://github.com/oldratlee/useful-scripts/releases

在bin下面有很多工具,我们这次只要show-busy-java-threads.sh

上传show-busy-java-threads脚本到服务器

# 一定赋予执行权限

chmod +x show-busy-java-threads

直接运行

./show-busy-java-threads 
技术图片

 

如下图所示,找到了CPU使用率前5高的线程,查找非常迅速。

从前面两个线程可以看出,与使用原生工具(jstack)看到的一样。

其他命令

与Arthas一样,show-busy-java-threads也有一些其他很好用的增强命令:

show-busy-java-threads

从所有的 Java进程中找出最消耗CPU的线程(缺省5个),打印出其线程栈。

 

show-busy-java-threads -c 3

-c 3:3为n,指定显示最耗cpu使用率前3的线程。

 

show-busy-java-threads -c 3 -p 17376

展示进程17376耗费CPU组多的3个线程;

-p 17376 :17376为进程PID,-p参数指定进程PID。

 

show-busy-java-threads -s 【指定jstack命令的全路径】

对于sudo方式的运行,JAVA_HOME环境变量不能传递给root,

而root用户往往没有配置JAVA_HOME且不方便配置,

显式指定jstack命令的路径就反而显得更方便了

 

show-busy-java-threads -a yao.log

将输出结果导入到指定文件yao.log中

 

show-busy-java-threads 3 5

每5秒执行一次,一共执行3次; 缺省执行一次,缺省间隔是3秒

总结

今天老顾介绍了3中方法排查服务器CPU负载过高的情况,主要流程是

1、查看CPU负载过高进程。

2、查看进程中负载高的线程。

3、获取进程中的堆栈信息。

4、获取堆栈中对应的线程信息,找到里面的问题方法。

在排查过程中我们不只使用了原生工具,还使用了增强工具Arthas与show-busy-java-threads,大大简化了我们排查的步骤。

其实增强工具无非就是在原生方法上面封装了而已,有很多这种工具都是在原生方法中封装而成。

 

以上是关于生产数据库服务器CPU使用率居高不下问题排查的主要内容,如果未能解决你的问题,请参考以下文章

利用阿里开源工具进行排查线上CPU居高问题 转

Mysql数据库CPU占用过高原因排查 show processlist

排查elasticsearch的cpu居高不下,查询慢的问题

记一次mycat服务异常排查

服务器挖矿病毒的排查过程

一次生产CPU100%问题排查