大家好,我是不才陈某~
[星球]()一位小伙伴面试了 网易,遇到了一个 性能类的面试题:CPU 飙升 900%,该怎么解决?
惋惜的是,以上的问题,这个小伙没有答复现实。
最终,导致他网易之路,终止在二面,十分惋惜
关注公众号:码猿技术专栏,回复关键词:1111 获取阿里外部 Java 性能调优手册!
首先,阐明一下问题:CPU 飙升 200% 以上是生产容易产生的场景
场景:1:MySQL 过程飙升 900%
大家在应用 MySQL 过程,想必都有遇到过 CPU 忽然过高,或者达到 200% 以上的状况。
数据库执行查问或数据批改操作时,零碎须要耗费大量的 CPU 资源保护从存储系统、内存数据中的一致性。
并发量大并且大量 SQL 性能低的状况下,比方字段是没有建设索引,则会导致疾速 CPU 飙升,如果还开启了慢日志记录,会导致性能更加好转。生产上有 MYSQL 飙升 900% 的顽劣状况。
场景 2:Java 过程飙升 900%
一般来说 Java 过程不做大量 CPU 运算,失常状况下,CPU 应该在 100~200% 之间,
然而,一旦高并发场景,要么走到了死循环,要么就是在做大量的 GC, 容易呈现这种 CPU 飙升的状况,CPU 飙升 900%,是齐全有可能的。
其余场景:其余的相似过程飙升 900% 的场景
比方 Redis、Nginx 等等。
陈某提醒:大家介绍场景的时候,就说本人次要波及了两个场景,Java 过程飙升 900%、MySQL 过程飙升 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 数据库优化的实在案例
陈某提醒:以下案例,来自互联网。大家参考一下,筹备一个本人的案例。
本问题亲身经历过。
之前开发共事编写的 SQL 语句,就导致过线上 CPU 过高,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 查问可能失常执行。
3、没隔一会,又产生大量的申请超时问题。接着进行剖析,发现是开启了 慢日志查问。大量的 SQL 查问语句超过慢日志设置的阀值,于是将慢日志敞开之后,速度霎时晋升。CPU 的使用率根本放弃在 300% 左右。但还不是现实状态。
4、紧接着将局部实时查问数据的 SQL 语句,都通过缓存 (redis) 读写实现。察看一段时间后,根本维持在了 70%~80%。
总结:其实本次事变的解决很简略,就是增加索引与缓存联合应用。
- 不举荐在这种 CPU 应用过高的状况下进行慢日志的开启。因为大量的申请,如果真是慢日志问题会产生日志磁盘写入,性能贼低。
- 间接通过 MySQL show processlist 命令查看,根本能清晰的定位出局部查问问题重大的 SQL 语句,在针对该 SQL 语句进行剖析。个别可能就是索引、锁、查问大量字段、大表等问题导致。
- 再则肯定要应用缓存零碎,升高对 MySQL 的查问频次。
- 对于内存调优,也是一种解决方案。
场景 2 开展:Java 过程 CPU 飙升到 900%,怎么解决?
定位过程:
CPU 飙升问题定位的个别步骤是:
- 首先通过 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% 优化的实在案例
陈某提醒:以下案例,来自互联网。大家参考一下,筹备一个本人的案例。
最近负责的一个我的项目上线,运行一段时间后发现对应的过程居然占用了 700% 的 CPU,导致公司的物理服务器都不堪重负,频繁宕机。
那么, 针对这类 java 过程 CPU 飙升的问题,咱们个别要怎么去定位解决呢?、
采纳 top 命令定位过程
登录服务器,执行 top 命令,查看 CPU 占用状况,找到过程的 pid
top
很容易发现,PID 为 29706 的 java 过程的 CPU 飙升到 700% 多,且始终降不下来,很显然呈现了问题。
应用 top -Hp 命令定位线程
应用 top -Hp 命令(为 Java 过程的 id 号)查看该 Java 过程内所有线程的资源占用状况(按 shft+ p 依照 cpu 占用进行排序,按 shift+ m 依照内存占用进行排序)
此处依照 cpu 排序:
top -Hp 23602
很容易发现,多个线程的 CPU 占用达到了 90% 多。咱们筛选线程号为 30309 的线程持续剖析。
应用 jstack 命令定位代码
1. 线程号转换 5 为 16 进制
printf“%x\n”命令(tid 指线程的 id 号)将以上 10 进制的线程号转换为 16 进制:
printf "%x\n" 30309
转换后的后果别离为 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
依据搜寻后果,判断应该是 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) // 业务代码阻塞点
剖析代码解决问题
上面是 ImageConverter.run()办法中的局部外围代码。
逻辑阐明:
/ 存储 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;
}
在 while 循环中,一直读取梗塞队列 dataQueue 中的数据,如果数据为空,则执行 continue 进行下一次循环。
如果不为空,则通过 poll()办法读取数据,做相干逻辑解决。
初看这段代码如同每什么问题,然而如果 dataQueue 对象长期为空的话,这里就会始终空循环,导致 CPU 飙升。
那么如果解决呢?
剖析 LinkedBlockingQueue 阻塞队列的 API 发现:
// 取出队列中的头部元素,如果队列为空则调用此办法的线程被阻塞期待,直到有元素能被取出,如果期待过程被中断则抛出 InterruptedException
E take() throws InterruptedException;
// 取出队列中的头部元素,如果队列为空返回 null
E poll();
这两种取值的 API,显然 take 办法更时候这里的场景。
代码批改为:
while (isRunning) {/* if (device.getMinicap().dataQueue.isEmpty()) {continue;}*/
byte[] buffer = new byte[0];
try {buffer = device.getMinicap().dataQueue.take();} catch (InterruptedException e) {e.printStackTrace();
}
……
}
重启我的项目后,测试发现我的项目运行稳固,对应我的项目过程的 CPU 耗费占比不到 10%。
起源:技术自在圈
最初说一句(别白嫖,求关注)
陈某每一篇文章都是精心输入,如果这篇文章对你有所帮忙,或者有所启发的话,帮忙 点赞 、 在看 、 转发 、 珍藏,你的反对就是我坚持下去的最大能源!
关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,能够退出技术探讨群,和大家一起探讨技术,吹牛逼!