大家好,我是不才陈某~
[星球]()一位小伙伴面试了 网易,遇到了一个 性能类的面试题: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);//生产线程@Overridepublic 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发现:
//取出队列中的头部元素,如果队列为空则调用此办法的线程被阻塞期待,直到有元素能被取出,如果期待过程被中断则抛出InterruptedExceptionE take() throws InterruptedException;//取出队列中的头部元素,如果队列为空返回nullE 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%。
起源:技术自在圈
最初说一句(别白嫖,求关注)
陈某每一篇文章都是精心输入,如果这篇文章对你有所帮忙,或者有所启发的话,帮忙点赞、在看、转发、珍藏,你的反对就是我坚持下去的最大能源!
关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,能够退出技术探讨群,和大家一起探讨技术,吹牛逼!