原文链接:https://blog.51cto.com/u_16213628/7368576
首先,说明一下问题:CPU飙升200% 以上是生产容易发生的场景
场景:1:MySQL进程飙升900%
场景2:Java进程飙升900%
其他场景:其他的类似进程飙升900%的场景
场景一:MySQL进程CPU飙升到900%,怎么处理?
-
使用top 命令观察,确定是mysqld导致还是其他原因。 -
如果是mysqld导致的,show processlist,查看session情况,确定是不是有消耗资源的sql在运行。 -
找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。
-
kill 掉这些线程(同时观察 cpu 使用率是否下降), 一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。 -
进行相应的调整(比如说加索引、改 sql、改内存参数)index 是否缺失,如果是,则 建立索引。也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等; -
优化的过程,往往不是一步完成的,而是一步一步,执行一项优化措辞,再观察,再优化。
场景1的真实案例:MySQL数据库优化的真实案例
-
经过询问,发现很多查询都是走MySQL,没有用到缓存。 -
既然没有用到缓存,则是大量请求全部查询MySQL导致。通过下面的命令查看:
- show processlist;
- select id form user where user_code = ‘xxxxx’;
- show index form user;
-
不推荐在这种CPU使用过高的情况下进行慢日志的开启。因为大量的请求,如果真是慢日志问题会发生日志磁盘写入,性能贼低。 -
直接通过MySQL show processlist命令查看,基本能清晰的定位出部分查询问题严重的SQL语句,在针对该SQL语句进行分析。一般可能就是索引、锁、查询大量字段、大表等问题导致。 -
再则一定要使用缓存系统,降低对MySQL的查询频次。 -
对于内存调优,也是一种解决方案。
场景2展开:Java进程CPU飙升到900%,怎么处理?
-
首先通过top指令查看当前占用CPU较高的进程PID; -
查看当前进程消耗资源的线程PID:top -Hp PID -
通过print命令将线程PID转为16进制,根据该16进制值去打印的堆栈日志内查询,查看该线程所驻留的方法位置。 -
通过jstack命令,查看栈信息,定位到线程对应的具体代码。 -
分析代码解决问题。
-
如果是空循环,或者空自旋。处理方式:可以使用Thread.sleep或者加锁,让线程适当的阻塞。 -
在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等。处理方式:可以减少对象的创建数量,或者,可以考虑使用 对象池。 -
其他的一些造成CPU飙升的场景,比如 selector空轮训导致CPU飙升 。处理方式:参考Netty源码,无效的事件查询到了一定的次数,进行 selector 重建。
Java的CPU 飙升700%优化的真实案例
采用top命令定位进程
- top
使用top -Hp命令定位线程
- top -Hp 23602
使用jstack命令定位代码
- printf “%x\n” 30309
- jstack -l 进程ID > ./jstack_result.txt
- jstack -l 29706 > ./jstack_result.txt
- cat jstack_result.txt |grep -A 100 7665
- 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) //业务代码阻塞点
分析代码解决问题
- /存储minicap的socket连接返回的数据 (改用消息队列存储读到的流数据) ,设置阻塞队列长度,防止出现内存溢出
//全局变量
private BlockingQueue<byte[]> dataQueue = new LinkedBlockingQueue<byte[]>(100000);
//消费线程
@Override
public void run() {
//long start = System.currentTimeMillis();
while (isRunning) {
//分析这里从LinkedBlockingQueue
if (dataQueue.isEmpty()) {
continue;
}
byte[] buffer = device.getMinicap().dataQueue.poll();
int len = buffer.length;
}
- //取出队列中的头部元素,如果队列为空则调用此方法的线程被阻塞等待,直到有元素能被取出,如果等待过程被中断则抛出InterruptedException
E take() throws InterruptedException;
//取出队列中的头部元素,如果队列为空返回null
E poll();
- while (isRunning) {
/* if (device.getMinicap().dataQueue.isEmpty()) {
continue;
}*/
byte[] buffer = new byte[0];
try {
buffer = device.getMinicap().dataQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
……
}
想了解更多干货,可通过下方扫码关注
详情咨询
可扫码添加上智启元官方客服微信👇