一、简答题
1、如果线上某台虚机CPU Load过高,该如何快速排查原因?只介绍思路和涉及的Linux命令即可 。
认识CPU 和 Load(负载)是什么?
答:
top命令是最常见的查看cpu和load的命令
记忆各个变量含义
- 第一行:
10:20:10 up 1 min : 当前系统时间,距离上次启动相差1分钟
1 user :当前登录一个用户
load average : 1.85, 0.84, 0.32 表示最近1分钟、5分钟、15分钟的系统平均负载 - 第二行:
Tasks:288 total : 当前总共有288个进程
running、 sleeping、stopped、zombie(僵尸进程):见名知意 - 第三行:
%Cpu(s) : 所有CPU的总体信息,按1会切换到每个CPU的信息。
us(用户空间占CPU的百分比), sy(系统空间。。), ni(优先级进程。。), id(空闲cpu。。) , wa(磁盘IO等待占CPU的百分比),hi(硬中断),si(软中断),st(虚拟CPU) - 第四行:内存
- 第五行:SWAP分区
CPU:cpu这个值反应的是某个采样时间内的cpu使用情况
Load:系统load是处于运行状态或者不可中断状态的进程的平均数。可以算入load的内容包括:一个处于运行状态的进程表示:正在使用cpu或者等待使用cpu,一个不可中断状态的进程:表示正在等待IO,进程中的线程数也是会被当作不同的进程来计算的。
load高、cpu高的排查思路
首先抛出一个观点:cpu高不是问题,由cpu高引起的load高才是问题,load是判断系统能力指标的依据
造成cpu load过高的原因: Full gc次数的增大、代码中存在Bug(例如死循环、正则的不恰当使用等)都有可能造成cpu load 增高。
- ps -ef | grep java; 查看Java进程的pid
- top -Hp [java进程号]:查看当前进程下最耗费CPU的线程
- printf “%x\n” [步骤2中的线程号]:得到线程的16进制表示
- jstack [java进程号] | grep -A100 [步骤3的结果]:查看线程堆栈,定位代码行。参考:如何使用JStack分析线程状态
load高、cpu低的排查思路
会导致load高的几个因素:
- 线程正在使用cpu
- 线程正在等待使用cpu
- 线程在执行不可被打断的IO操作
**主要原因:**在cpu不高的情况下假如load高,大概率io高才是罪魁祸首,它导致的是任务一直在跑,迟迟处理不完,线程无法回归线程池中。
步骤:
- 1、首先确认一下wa指标,是否真的是等待io过大
- 2、如果是,根据cpu的排查步骤排查一遍,查看IO部分的代码进行分析。
Java应用load高的几种原因总结:
- 死循环或者大量不合理的循环操作。
- 频繁的YoungGC
- 频繁的FullGC
- 高磁盘IO
- 高网络IO
真对上面的几点原因,大体思路:
- top先查看用户us与空闲us(id)的cpu占比。目的是确认load高是否是cpu引起的
- 如果是cpu引起的,那么确认一下是否gc引起,jstack命令 + gc日志分析
- gc引起的直接dump,非gc引起的分析线程堆栈
- 如果不是高cpu引起的,查看磁盘io占比(wa),如果是打印线程堆栈分析是否有大量的文件io
- 如果不是高cpu引起的,且不是磁盘io引起的,检查依赖子系统的调用耗时,例如高耗时的网络调用。
完整解释请点击。
2、 请简要描述MySQL数据库联合索引的命中规则,可举例说明。
1、MySQL联合索引遵循最左前缀匹配原则。即从联合索引的左边开始向右匹配,直到遇到匹配终止条件。
例如联合索引(col1, col2, col3),那么where条件后可以为:col1 = ‘a’ and col2 = ‘b’ 可命中联合索引的(col1,col2)前缀部分,where col2 = ‘b’ and col3 = ‘c’ 不符合最左前缀原则,不能命中联合索引。
2、匹配终止条件为范围条件(如:>、<、between、like)或函数等不能应用索引情况。例如where col1 = ‘a’ and col2 > ‘b’ and col3 = ‘c’ 只能用到col1。
3、where条件中的顺序不影响
3、请描述https的请求过程。
4、什么是事务传播行为?你知道Spring事务中都有哪些传播类型吗?如何使用/指定传播类型?
以下是自己学习记录的笔记,有点混乱。推荐看这个博客讲的非常清晰戳这里
- 什么是事务传播行为:
事务传播用于描述当一个有事务传播行为修饰的方法被嵌套进另一个方法的事务如何传播。(注意:谁被嵌套进谁)
//事务A没有使用@Transaction(Propagation = XXX)修饰
public transactionA() {
transactionB();
}
@Transaction(Propagation = XXX)
public transactionB(){
}
事务B的事务传播行为有方法上的注解@Transaction(Propagation = XXX)决定。而事务A并没有开启事务,所以一个事务的传播行为修饰的方法并不是必须要在开启事务的外层的方法中调用。
- Spring中其中事务传播:
– ①、PROPAGATION_REQUIRED:如果当前调用方法没有事务,则创建一个新事务,如果有则加入到当前事务中。
其中具体验证请参考
验证解释:
Ⅰ、外层没有开启事务,内部用PROPAGATION_REQUIRED修饰的方法开启自己新的事务并且事务相互独立,互不干扰。
Ⅱ、外层开启事务,内部的方法加入到了外层的方法事务中,只要外层和内部方法是同一个事务,只要有一个回滚,整个事务回滚。
– ②、PROPAGATION_SUPPORTS:支持当前事务,如果外层方法没有事务,那内部的事务就以非事务的方式运行。
– ③、PROPAGATION_MANDATORY:支持当前事务,如果外层方法没有事务,就直接抛出异常
– ④、PROPAGATION_REQUIRES_NEW:新建事务,如果外层方法有事务,就把外层方法的事务挂起,先执行内部的新事务
验证解释:
Ⅰ、外层没有开启事务,Propagation.REQUIRES_NEW修饰的内部方法新开启自己的事务,各自的事务相互独立,互不干扰。
Ⅱ、外层开启事务,内部方法依然会开启新的事务,只不过需要先将外部的事务挂起,先执行内部事务,如果内部事务抛出了异常回滚后,异常继续向外抛出,如果在外部方法内被捕获处理了就不会影响外层事务,如果没有捕获,则外层事务收到异常,也继续回滚。
– ⑤、PROPAGATION_NOT_SUPPORTED:用此关键字修饰的内部方法以非事务的方式运行,如果外层方法开启的事务则挂起事务。
– ⑥、PROPAGATION_NEVER:以非事务的方式运行,如果存在事务,直接抛出异常。
– ⑦、PROPAGATION_NESTED:如果外层方法存在事务,则在嵌套内执行。如果外层方法没有事务,则执行与Propagation.REQUIRED类似的操作。
验证解释:
Ⅰ、外层没有开启事务,则和required修饰的方法一样,各自新建独立的事务,独立执行。
Ⅱ、外层开启了事务,外层事务回滚,子事务一定回滚。子事务可以单独回滚不影响外层事务和其他子事务。
required、requires_new、nested的区别
-
required:
①、如果外部开启了事务,因为是同一个事务,所以无论外部方法是否捕获都会发生回滚。
②、如果外部没有开启事务,因为是新建的事务,所以外部事务无论是否捕获异常,只有内部事务会回滚。
③、如果required作用的是外部方法,而内部方法没有传播修饰,内部方法出现异常,则区别就是是否捕获异常;如果捕获了异常则不会发生回滚。如果没有捕获异常则发生回滚。 -
requires_new:
①、 始终是独立的事务,外部方法传播行为是required的内部是requires_new时,当内部出现异常,如果外部不捕获则内外一起回滚,如果外部方法捕获了,则只有内部的事务回滚。
②、外部发生异常只会回滚外部事务。 -
nested:
①、外部有传播行为,内部的传播行为是nested时,内部方法出现异常时,外部捕获异常只回滚内部事务,如果不捕获异常,则内外一起回滚,因为是事务嵌套。
②、当外部事务发生异常则全部回滚。
requires_new和nested的主要区别:由于requires_new是独立事务,nested是父子事务嵌套,所以最主要的区别就是:
- REQUIRES_NEW:因为会挂起外层事务,并且两个事务独立,所以子事务一定会先外层事务提交。这样当外层事务发生回滚时,就会出现脏数据
- NESTED:因为两个是嵌套事务,所以子事务会等待外层事务一起提交。