[JVM] CPU飙升怎么办

292 阅读4分钟

1. MySql CPU飙升

1.1 产生原因

数据库执行查询或数据修改操作时,系统需要消耗大量的CPU资源维护存储系统和内存数据中的一致性。 并发量大并且大量SQL性能低的情况下,比如字段没有建立索引,会导致快速CPU飙升,如果还开启了慢日志记录,会导致性能更加恶化。 也就是产生的原因可能有:并发量大,SQL性能低,开启慢日志记录。

1.2 定位

(1)使用top命令观察,确定是mysqld导致还是其他原因; (2)如果是mysqld导致的,show processlist,查看session情况,确定是不是有消耗资源的sql在运行; (3)找出消耗高的sql,看看执行计划是否准确,index是否缺失,或者实在是数据量太大造成。

1.3 解决办法

(1)kill掉这些线程(同时观察cpu使用率是否下降), 一般来说,肯定要kill掉这些线程(同时观察cpu使用率是否下降),等进行相应的调整(比如说加索引、改sql、改内存参数)之后,再重新跑这些 SQL; (2)进行相应的调整(比如说加索引、改sql、改内存参数)index是否缺失,如果是,则建立索引。 也有可能是每个sql 消耗资源并不多,但是突然之间,有大量的session连进来导致cpu飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等。

1.4 案例

MySQL的CPU使用率达到900%+,通过优化最后降低到70%~80%。

首先,我们要对问题定位而不是盲目的开启什么慢日志,在并发量大并且大量SQL性能低的情况下,开启慢日志无疑是将MySQL推向崩溃的边缘。

当时遇到这个情况,分析了当前的数据量、索引情况、缓存使用情况。目测数据量不大,也就几百万条而已。接下来就去定位索引、缓存问题。

很多查询都是走MySQL,没有用到缓存。 既然没有用到缓存,则是大量请求全部查询MySQL导致。通过下面的命令查看:

show processlist;

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

select id form user where user_code = 'xxxxx';

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

show index form user;

发现这个字段是没有建立索引。增加索引之后,该条SQL查询能够正常执行。

没隔一会,又发生大量的请求超时问题。接着进行分析,发现是开启了 慢日志查询。大量的SQL查询语句超过慢日志设置的阈值,于是将慢日志关闭之后,速度瞬间提升。CPU的使用率基本保持在300%左右。但还不是理想状态。

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

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

2. JAVA进程CPU飙升

2.1 产生原因

死循环 做大量的GC 并发量高于预期

2.2 问题定位

  1. 首先通过top指令查看当前占用CPU较高的进程PID(按shft+p按照cpu占用进行排序,按shift+m按照内存占用进行排序);
  2. 查看当前进程消耗资源的线程PID:top -Hp PID;
  3. 通过print命令将线程PID转为16进制,根据该16进制值去打印的堆栈日志内查询,查看该线程所驻留的方法位置; printf “%x\n” tid
  4. 通过jstack命令,查看栈信息,定位到线程对应的具体代码; jstack -l 进程ID > ./jstack_result.txt cat jstack_result.txt |grep -A 100 7665
  5. 分析代码解决问题。

2.3 解决办法

如果是空循环,或者空自旋,可以使用Thread.sleep或者加锁,让线程适当的阻塞。 在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等,可以减少对象的创建数量,或者,可以考虑使用 对象池。