社群一位小伙伴面试了 网易,狂飙遇到了一个 性能类的该处面试题:
可惜的网易是,以上的狂飙问题,这个小伙没有回答理想。该处
最终,网易导致他网易之路,狂飙终止在二面,该处非常可惜
现在把这个 题目,以及参考答案,收入咱们的
《Java面试宝典 PDF》,供后面的小伙伴参考,前车之鉴啊
注:本文以 PDF 持续更新,最新Java 架构笔记、面试题 的PDF文件,请后台私信【笔记】获取哦
大家在使用MySQL过程,想必都有遇到过CPU突然过高,或者达到200%以上的情况。
数据库执行查询或数据修改操作时,系统需要消耗大量的CPU资源维护从存储系统、内存数据中的一致性。
并发量大并且大量SQL性能低的情况下,比如字段是没有建立索引,则会导致快速CPU飙升,如果还开启了慢日志记录,会导致性能更加恶化。生产上有MYSQL 飙升900% 的恶劣情况。
一般来说Java 进程不做大量 CPU 运算,正常情况下,CPU 应该在 100~200% 之间,
但是,一旦高并发场景,要么走到了死循环,要么就是在做大量的 GC, 容易出现这种 CPU 飙升的情况,CPU飙升900%,是完全有可能的。
比如Redis、Nginx等等。
尼恩提示:大家介绍场景的时候,就说自己主要涉及了两个场景, Java进程飙升900%、MySQL进程飙升900%两种场景,其实,这两个场景就足够讲半天了, 其他的,使用规避技巧规避一下就行。
定位过程:
处理过程:
尼恩提示:以下案例,来自互联网。大家参考一下,准备一个自己的案例。
本问题亲身经历过。
之前开发同事编写的SQL语句,就导致过线上CPU过高,MySQL的CPU使用率达到900%+,通过优化最后降低到70%~80%。下面说说个人在这个过程中的排查思路。
首先,我们要对问题定位而不是盲目的开启什么 慢日志,在并发量大并且大量SQL性能低的情况下,开启慢日志无意是将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飙升问题定位的一般步骤是:
处理过程:
尼恩提示:以下案例,来自互联网。大家参考一下,准备一个自己的案例。
最近负责的一个项目上线,运行一段时间后发现对应的进程竟然占用了700%的CPU,导致公司的物理服务器都不堪重负,频繁宕机。
那么,针对这类java进程CPU飙升的问题,我们一般要怎么去定位解决呢?、
登录服务器,执行top命令,查看CPU占用情况,找到进程的pid
top
很容易发现,PID为29706的java进程的CPU飙升到700%多,且一直降不下来,很显然出现了问题。
使用 top -Hp <pid> 命令(为Java进程的id号)查看该Java进程内所有线程的资源占用情况(按shft+p按照cpu占用进行排序,按shift+m按照内存占用进行排序)
此处按照cpu排序:
top -Hp 23602
很容易发现,多个线程的CPU占用达到了90%多。我们挑选线程号为30309的线程继续分析。
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%。
责任编辑:武晓燕 来源: 今日头条 CPUSQL性能
(责任编辑:热点)
天保基建(000965.SZ):2020年净利降49.78% 基本每股收益0.0859元
Linux 过去 3 年大幅优化 AMD Ryzen 处理器,综合性能提升 15%
基石科技控股(08391.HK)完成配发6962.5万股 每股0.40港元
OpenHarmony富设备移植指南(五)打包刷机与简单设备调试