生产环境上出现 CPU 性能问题是非常典型的一类问题,往往这个时候就比较考验相关人员排查问题的能力
我相信不少小伙伴在工作当中多多少少都会碰到 CPU 出现性能瓶颈
不知道小伙伴们有没有跟我一样的感受——当 CPU 出现性能瓶颈的时候,会有一种“拔剑四顾心茫然”的感觉,CPU 性能指标这么多,CPU性能分析工具也是一抓一大把
该观察哪些性能指标?又该用什么工具来进行观察?你会发现排查问题往往比解决问题更难
那么今天就给大家分享一下咸鱼熬夜整理的 CPU 性能排查套路,告诉大家如何“快准狠”地找到问题所在,让大家在面对 CPU 出现性能瓶颈的时候不再手足无措
CPU 使用率
当 CPU 出现性能瓶颈的时候,最容易想到的就是 CPU 使用率,这也是生产环境中最常见的一个性能指标
CPU使用率是指在单位时间内CPU处在非空闲态的时间比,反映了CPU的繁忙程度
比如说单核 CPU 一秒内处在非空闲态的时间为 0.6s,那么这时候的 CPU 使用率就是 60%;双核 CPU 一秒内处在非空闲态的时间分别为 0.6s 和 0.4s,那么 CPU 使用率为(0.4+0.6)/ 2 * 100% = 50%
根据 CPU 上运行的任务的不同,又被分为用户 CPU、系统 CPU、等待 I/O CPU、软中断和硬中断等
- 用户 CPU 使用率
包括用户态 CPU 使用率(user)和低优先级用户态 CPU 使用率(nice)
表示 CPU 处在用户态运行的时间的百分比,用户 CPU 使用率高,通常说明用户进程比较忙,占用了较多的 CPU 时间,着重排查进程的性能问题
- 系统 CPU 使用率
表示 CPU 处在内核态运行的时间的百分比(不包括中断),系统 CPU 使用率高,说明内核比较繁忙,占用了较多的 CPU 时间,着重排查内核线程或者系统调用的性能问题
- 等待 I/O 的 CPU 使用率
通常称作 iowait,表示系统等待 I/O 的时间的百分比
iowait 高,通常说明系统与硬件设备的 I/O 交互时间比较长,应该着重排查系统存储设备是不是出现了 I/O 问题
- 软中断和硬中断的 CPU 使用率
分别表示内核调用软中断处理程序,硬中断处理程序的时间的百分比
这两个指标高,通常说明系统发生了大量的中断,着重排查内核的中断服务程序
尤其是是软中断 CPU 使用率过高,是很常见的一种 CPU 性能问题
在 Linux 中,每个 CPU 都对应着一个软中断内核线程,名字是 ksoftirqd/CPU 编号
当软中断事件的频率过高时,就会因为软中断 CPU 使用率过高导致内核线程无法及时处理软中断,进而引发像网络收发延迟,调度缓慢等性能问题
在这里我举个软中断使用率升高的例子:
当计算机网卡接收到数据后,会将数据先放到缓冲区中,然后触发中断,通知内核来处理数据,这是一个异步的过程,在等待内核处理数据的过程中网卡是可以继续接收网络数据的
因为有缓冲区的参与,我们可以将网卡看成生产者,内核看出消费者,而缓冲区就是消息队列
生产者生产消息速度过快(网卡不断接收数据不断触发中断),消费者消费速度过慢(内核还未来得及处理 A 中断,B 中断就又来了),就会引起消息挤压和消息处理延时问题
平均负载
第二个要注意的性能指标,就是平均负载(Load Average)了
平均负载指的是单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率没有直接关系
它反应了系统的整体负载情况,主要包括三个数值,分别指过去 1 分钟、过去 5 分钟和过去 15 分钟的平均负载
# uptime
load average: 0.00, 0.01, 0.05
既然平均的是活跃进程数,那么理想情况下就是每个 CPU 上都刚好运行着一个进程,这样每个 CPU 都得到了充分利用
比如当平均负载为2时意味着什么呢?
- 如果 CPU 为两个,意味着所有的 CPU 刚好被完全占用
- 如果 CPU 为四个,意味着有 50% 的 CPU 是空闲的
- 如果CPU 为一个,意味着有一半的进程竞争不到 CPU
那么生产环境上的 CPU 平均负载为多少比较合理呢?
我们知道,平均负载最理想的状况是等于逻辑 CPU 个数,所以我们要首先知道系统有几个 CPU
知道了 CPU 个数之后,我们就可以判断出平均负载比 CPU 个数还大时,系统已经出现了过载
但是这样就结束了吗?且慢,我们从上面 uptime 输出的结果可以看到,平均负载有三个数值,我们该参考哪一个呢?
实际上我们三个都要看,三个不同时间间隔的平均值其实给我们提供了系统负载的趋势
1、如果1 分钟、5 分钟、15 分钟的三个值基本相同,或者相差不大,那就说明系统负载很平稳
2、如果 1 分钟的值远小于 15 分钟的值,就说明系统最近 1 分钟的负载在减少,而过去 15 分钟内却有很大的负载
3、如果 1 分钟的值远大于 15 分钟的值,就说明最近 1 分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,所以就需要持续观察。一旦 1 分钟的平均负载接近或超过了 CPU 的个数,就意味着系统正在发生过载的问题,这时就得分析调查是哪里导致的问题,并要想办法优化了
举个例子:
假设在单个 CPU 的系统上观察到平均负载为:1.89、0.60、7.98。那么说明在过去一分钟内系统有 73% 的超载,但是在15分钟内,系统有 689% 的超载,从时间线来看系统的负载正在降低
在生产环境中,当观察到系统负载有明显升高的趋势时(比如说负载翻倍了)就要着重进行排查是哪些进程导致负载变高了
进程上下文切换
第三个要注意的性能指标,就是进程的上下文切换
在介绍进程上下文切换之前,我们先来看看 CPU 上下文切换
Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行,当然这些任务并不是真的在同时运行,而是因为系统在极短时间内将 CPU 轮流分配给他们,造成宏观上多任务同时进行的错觉
而在轮流运行每个任务前,CPU 都需要知道任务从哪里加载,又从哪里开始运行。即 CPU 需要实现设置好 CPU 寄存器和程序计数器
CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU 上下文
知道了什么是 CPU 上下文之后,CPU 上下文切换也就很好理解了
CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务
而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行
根据任务的不同,CPU 的上下文切换就可以分为几个不同的场景,也就是进程上下文切换、线程上下文切换以及中断上下文切换
- 进程上下文切换
进程上下文切换,是指从一个进程切换到另一个进程运行
我们知道,进程是由内核来管理和调度的,进程的切换只能发生在内核态,而内核需要在 CPU 上运行才能完成
所以说,过多的上下文切换,会将原本运行进程的 CPU 时间,消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成为性能瓶颈
既然知道进程上下文切换有可能导致 CPU 性能问题,那么什么时候会出现进程上下文切换呢?
- 为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行
- 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行
- 进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,会重新调度
- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行
- 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序
一旦出现上下文切换的性能问题,就要着重分析是以上哪个场景导致的
总结
本篇文章介绍了三个常用的 CPU 性能指标,当 CPU 出现性能瓶颈的时候,就可以根据这些性能指标进行逐个排查分析
前提是需要对每个性能指标背后的原理要熟悉,只有这样才能够缩小排查范围,提高效率,既省时又省力
举个例子,当你观察到用户 CPU 使用率升高的时候,我们应该去着重排查进程的用户态而不是内核态,因为用户 CPU 使用率反映的就是用户态的 CPU 使用情况,而内核态的 CPU 使用情况只会反映到系统 CPU 使用率上