又一次生产 CPU 高负载排查实践

标签: 问题排查 Java 进阶 Java Thread | 发表时间:2019-06-18 02:36 | 作者:
出处:http://crossoverjie.top/

前言

前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨。

其实早在去年我也处理过类似的问题,并记录下来: 《一次生产 CPU 100% 排查优化实践》

不过本次问题产生的原因却和上次不太一样,大家可以接着往下看。

问题分析

收到邮件后我马上登陆那台服务器,看了下案发现场还在(负载依然很高)。

于是我便利用这类问题的排查套路定位一遍。


首先利用 top -c 将系统资源使用情况实时显示出来 ( -c 参数可以完整显示命令)。

接着输入 大写 P 将应用按照 CPU 使用率排序,第一个就是使用率最高的程序。

果不其然就是我们的一个 Java 应用。

这个应用简单来说就是定时跑一些报表使的,每天凌晨会触发任务调度,正常情况下几个小时就会运行完毕。


常规操作第二步自然是得知道这个应用中最耗 CPU 的线程到底再干嘛。

利用 top -Hp pid 然后输入 P 依然可以按照 CPU 使用率将线程排序。

这时我们只需要记住线程的 ID 将其转换为 16 进制存储起来,通过 jstack pid >pid.log 生成日志文件,利用刚才保存的 16 进制进程 ID 去这个线程快照中搜索即可知道消耗 CPU 的线程在干啥了。

如果你嫌麻烦,我也强烈推荐阿里开源的问题定位神器 arthas 来定位问题。

比如上述操作便可精简为一个命令 thread -n 3 即可将最忙碌的三个线程快照打印出来,非常高效。

更多关于 arthas 使用教程请参考 官方文档

由于之前忘记截图了,这里我直接得出结论吧:

最忙绿的线程是一个 GC 线程,也就意味着它在忙着做垃圾回收。

GC 查看

排查到这里,有经验的老司机一定会想到:多半是应用内存使用有问题导致的。

于是我通过 jstat -gcutil pid 200 50 将内存使用、gc 回收状况打印出来(每隔 200ms 打印 50次)。

从图中可以得到以下几个信息:

  • Eden 区和 old 区都快占满了,可见内存回收是有问题的。
  • fgc 回收频次很高,10s 之内发生了 8 次回收( (866493-866485)/ (200 *5))。
  • 持续的时间较长,fgc 已经发生了 8W 多次。

内存分析

既然是初步定位是内存问题,所以还是得拿一份内存快照分析才能最终定位到问题。

通过命令 jmap -dump:live,format=b,file=dump.hprof pid 可以导出一份快照文件。

这时就得借助 MAT 这类的分析工具出马了。

问题定位

通过这张图其实很明显可以看出,在内存中存在一个非常大的字符串,而这个字符串正好是被这个定时任务的线程引用着。

大概算了一下这个字符串所占的内存为 258m 左右,就一个字符串来说已经是非常大的对象了。

那这个字符串是咋产生的呢?

其实看上图中的引用关系及字符串的内容不难看出这是一个 insertSQL 语句。

这时不得不赞叹 MAT 这个工具,他还能帮你预测出这个内存快照可能出现问题地方同时给出线程快照。

最终通过这个线程快照找到了具体的业务代码:

他调用一个写入数据库的方法,而这个方法会拼接一个 insert 语句,其中的 values 是循环拼接生成,大概如下:

     
1
2
3
4
5
6
7
     
<insert id="insert" parameterType="java.util.List">
insert into xx (files)
values
<foreach collection="list" item="item" separator=",">
xxx
</foreach>
</insert>

所以一旦这个 list 非常大时,这个拼接的 SQL 语句也会很长。

通过刚才的内存分析其实可以看出这个 List 也是非常大的,也就导致了最终的这个 insert 语句占用的内存巨大。

优化策略

既然找到问题原因那就好解决了,有两个方向:

  • 控制源头 List 的大小,这个 List 也是从某张表中获取的数据,可以分页获取;这样后续的 insert 语句就会减小。
  • 控制批量写入数据的大小,其实本质还是要把这个拼接的 SQL 长度降下来。
  • 整个的写入效率需要重新评估。

总结

本次问题从分析到解决花的时间并不长,也还比较典型,其中的过程再总结一下:

  • 首先定位消耗 CPU 进程。
  • 再定位消耗 CPU 的具体线程。
  • 内存问题 dump 出快照进行分析。
  • 得出结论,调整代码,测试结果。

最后愿大家都别接到生产告警。

你的点赞与分享是对我最大的支持

相关 [生产 cpu 负载] 推荐:

又一次生产 CPU 高负载排查实践

- - crossoverJie's Blog
前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨. 其实早在去年我也处理过类似的问题,并记录下来: 《一次生产 CPU 100% 排查优化实践》. 不过本次问题产生的原因却和上次不太一样,大家可以接着往下看. 收到邮件后我马上登陆那台服务器,看了下案发现场还在(负载依然很高).

java问题导致linux负载、cpu过高如何定位

- - CSDN博客推荐文章
1.用top找到最耗资源的进程id. 2.查询最消耗资源的java进程. 3.打印java 栈 信息. 4.将耗资源的javaPID转换为16进制(5920转1720<16进制>  去百度找 :十进制转十六进制). PID 对应 堆栈中的nid(16进制). 去stack.txt 中查找nid=1720的问题.

CPU 使用率低高负载的原因,看看这篇!

- - SegmentFault 最新的文章
产生的原因一句话总结就是:等待磁盘I/O完成的进程过多,导致进程队列长度过大,但是cpu运行的进程却很少,这样就体现到负载过大了,cpu使用率低. 下面内容是具体的原理分析:. 在分析负载为什么高之前先介绍下什么是负载、多任务操作系统、进程调度等相关概念. 什么是负载:负载就是cpu在一段时间内正在处理以及等待cpu处理的进程数之和的统计信息,也就是cpu使用队列的长度统计信息,这个数字越小越好(如果超过CPU核心*0.7就是不正常).

小改动大效果:记一次CPU负载高问题排查和解决

- We_Get - 博客园-首页原创精华区
      问题缘起:收到运维同事发来的邮件,说自上次网站更新后,CPU使用率上升趋势明显(下图中红框部分所示),但网站访问数并没有增加.       问题排查:是什么原因导致CPU使用率上升呢. 肯定是某个访问量比较大的页面进行了耗CPU的操作,如文件读写、内存中的一些复杂运算等. 结合上次网站更新内容,将问题锁定在了房源详情页.

生产环境下JAVA进程高CPU占用故障排查

- - 开源软件 - ITeye博客
生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 1,程序属于CPU密集型,和开发沟通过,排除此类情况. 2,程序代码有问题,出现死循环,可能性极大. 1,开发那边无法排查代码某个模块有问题,从日志上也无法分析得出.

一次生产 CPU 100% 排查优化实践

- - crossoverJie's Blog
到了年底果然都不太平,最近又收到了运维报警:表示有些服务器负载非常高,让我们定位问题. 还真是想什么来什么,前些天还故意把某些服务器的负载提高( 没错,老板让我写个 BUG. ),不过还好是不同的环境互相没有影响. 拿到问题后首先去服务器上看了看,发现运行的只有我们的 Java 应用. 于是先用 ps 命令拿到了应用的 PID.

[MySQL优化案例]系列 — 典型性索引引发CPU负载飙升问题

- - MySQL中文网 - 叶金荣的技术和生活
收到一个mysql服务器负载告警,上去一看,load average都飙到280多了,用top一看,CPU跑到了336%,不过IO和内存的负载并不高,根据经验,应该又是一起索引引起的惨案了. 看下processlist以及slow query情况,发现有一个SQL经常出现,执行计划中的扫描记录数看着还可以,单次执行耗时为 0.07s,还不算太大.

生产上数据库大量的latch free 导致的CPU资源耗尽的问题的解决

- - CSDN博客推荐文章
中午的时候,我们生产上的某个数据库,cpu一直居高不下. 通过如下的sql语句,我们查看当时数据库的等待,争用的情况:. 从event可以看到,是latch 的争用导致的原因. 通过如果的sql,查看是什么样的latch. P2就是 这个latch的name,通过v$latchname这个视图就可以知道哪个具体的latch.

8086 CPU 寄存器简介

- 田野 - 博客园-首页原创精华区
打算写几篇稍近底层或者说是基础的博文,浅要介绍或者说是回顾一些基础知识,. 自然,还是得从最基础的开始,那就从汇编语言开刀吧,. 从汇编语言开刀的话,我们必须还先要了解一些其他东西,. 像  CPU ,内存这些知识点还是理解深刻一点的比较好,. 所以这一篇博文就绕着 80x86  CPU 中寄存器的基础部分下手,至于其他的一些将会在后续的博文中介绍.

CPU架构:i386,x86,AMD64

- - 脚本爱好者
IA32 : 32 bits Intel Architecture (32位带宽Intel构架). IA64 : 64 bits Intel Architecture (64位带宽Intel构架). i386 : Intel 386 ( 老的386机器,也泛指IA32体系的CPU). i586 : Intel 586 ( Pentium ,K6 级别CPU ).