每次发现系统变慢时,我们通常做的第一件事情,就是执行top
或者uptime
命令,来了解系统的负载情况,比如:
oceanstar@oceanstar:~$ uptime
22:59:50 up 3 min, 1 user, load average: 0.96, 0.95, 0.42
平均负载可以说是最常见,也是最重要的系统指标。那么什么是平均负载呢?
平均负载,是指单位时间内,系统处于可运行状态和不可中断状态的平均进程树,也就是平均活跃进程数,他和CPU使用率并没有直接关系
ps
命令看到的,处于R状态(Running、Runnable)的进程ps
命令看到的处于D状态(Uninterruptible Sleep,也称为Disk Sleep)的进程因此,你可以理解为,平均负载其实就是平均活跃进程数。平均活跃进程数,直观上的理解就是单位时间内的活跃进程数,但它实际上是活跃进程数的指数衰减平均值。这个“指数衰减平均”的详细含义不用去计较,这只是系统的一种更快速的计算方式,把它直接当初活跃进程数的平均值即可。
既然平均的是活跃进程数量,那么最理想的是,每个CPU上都刚好运行着一个进程,这样每个CPU都得到了充分利用。比如,当平均负载为2时,意味着什么呢?
问题是:怎么判断出,在
uptime
命令的结果里,那三个时间段的平均负载数,多大的时候能说明系统负载高?多小的时候能说明系统负责很低呢?
我们知道,平均负载最理想的情况是等于CPU个数。所以在评判瓶颈负载的时候,第一步是需要知道系统有几个CPU,这可以通过top
命令或者从/proc/cpuinfo
中读取,比如:
$ grep 'model name' /proc/cpuinfo | wc -l
4
有了CPU个数,我们就能判断出,当平均负载比CPU个数大的时候,系统已经出现了过载
第二个问题:平均负载有三个值,到底应该参考哪一个呢?
实际上,都要看。打个⽐⽅,就像初秋时北京的天⽓,如果只看中午的温度,你可能以为还在7⽉份的⼤夏天呢。但如果你结合了早上、中午、晚上三个时间点的温度来看,基本就可以全⽅位了解这⼀天的天⽓情况了
这上不同时间间隔的平均值,可以让我们分析系统负载趋势:
举个例子,假设我们在一个单CPU系统上看到平均负载为1.73、0.60、7.98,那么说明在过去1分钟内,系统有73%的超载,而在15分钟内,有698%的超载,从整体趋势来看,系统的负载在降低。
第三个问题:在实际生成环境中,平均负载多高时,需要我们中断关注呢?
当平均负载高于CPU数量70%的时候,你就应该分析排查负载高的问题了。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能
但70%这个数字并不是绝对的,最推荐的⽅法,还是把系统的平均负载监控起来,然后根据更多的历史数据,判断负载的变化
趋势。当发现负载有明显升⾼趋势时,⽐如说负载翻倍了,你再去做分析和调查。
下⾯,我们以三个示例分别来看这三种情况,并⽤ iostat、mpstat、pidstat 等⼯具,找出平均负载升⾼的根源。
stress
:是一个Linyx系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景sysstat
包含了常用的Linux性能⼯具,⽤来监控和分析系统的性能。我们的案例会⽤到这个包的两个命令 mpstat 和pidstat。
mpstat
是⼀个常⽤的多核 CPU 性能分析⼯具,⽤来实时查看每个 CPU 的性能指标,以及所有CPU的平均指标。pidstat
是⼀个常⽤的进程性能分析⼯具,⽤来实时查看进程的 CPU、内存、I/O 以及上下⽂切换等性能指标此外,每个场景都需要你开三个终端,登录到同⼀台 Linux 机器中。
另外要注意,下⾯的所有命令,我们都是默认以 root ⽤户运⾏。
如果上⾯的要求都已经完成了,你可以先⽤ uptime 命令,看⼀下测试前的平均负载情况:
$ uptime
... load average: 0.04, 0.02, 0.00
⾸先,我们在第⼀个终端运⾏ stress 命令,模拟⼀个 CPU 使⽤率 100% 的场景:
# --cpu 1: 指的是测试1个cpu --- 产生1个进程 每个进程都反复不停的计算随机数的平方根
# -t --timeout 600 : 指定运行600秒后停止
# stress测试cpu,是在用户态将cpu 耗尽。
$ stress --cpu 1 --timeout 600
stress: info: [3629] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
接着,在第⼆个终端运⾏uptime查看平均负载的变化情况:
# -d 参数表示⾼亮显示变化的区域
$ watch -d uptime
..., load average: 1.00, 0.75, 0.39
最后,在第三个终端运⾏mpstat查看 CPU 使⽤率的变化情况:
# -P ALL 表示监控所有CPU,后⾯数字5表示间隔5秒后输出⼀组数据
$ mpstat -P ALL 5
Linux 5.8.0-48-generic (oceanstar) 2021年10月20日 _x86_64_ (4 CPU)
23时58分58秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
23时59分03秒 all 25.15 0.00 0.00 0.00 0.00 0.05 0.00 0.00 0.00 74.80
23时59分03秒 0 0.00 0.00 0.00 0.00 0.00 0.20 0.00 0.00 0.00 99.80
23时59分03秒 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
23时59分03秒 2 0.80 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.20
23时59分03秒 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
从终端二可以看到,1分钟的平均负载会慢慢增加到1.00,而从终端三中还可以看到,正好有一个CPU的使用率为100%,但是它的iowait只有0。这说明,平均负载的升高正是由于CPU使用率为100%
那么,到底是哪个进程导致了 CPU 使⽤率为 100% 呢?你可以使⽤ pidstat
来查询:
# 间隔5s后输出一组数据,-u表示CPU指标
$ pidstat -u 5 1
Linux 5.8.0-48-generic (oceanstar) 2021年10月21日 _x86_64_ (4 CPU)
19时28分26秒 UID PID %usr %system %guest %wait %CPU CPU Command
19时28分31秒 0 679 0.00 0.20 0.00 0.00 0.20 3 vmtoolsd
19时28分31秒 1000 2187 0.40 1.00 0.00 0.40 1.40 3 Xorg
19时28分31秒 1000 2318 1.20 0.80 0.00 0.20 2.00 1 gnome-shell
19时28分31秒 1000 2659 0.60 0.20 0.00 0.20 0.80 3 gnome-terminal-
19时28分31秒 0 8133 0.20 0.00 0.00 0.00 0.20 1 mpstat
19时28分31秒 0 8153 100.00 0.00 0.00 0.00 100.00 2 stress
19时28分31秒 1000 8231 0.00 0.20 0.00 0.00 0.20 0 pidstat
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 679 0.00 0.20 0.00 0.00 0.20 - vmtoolsd
平均时间: 1000 2187 0.40 1.00 0.00 0.40 1.40 - Xorg
平均时间: 1000 2318 1.20 0.80 0.00 0.20 2.00 - gnome-shell
平均时间: 1000 2659 0.60 0.20 0.00 0.20 0.80 - gnome-terminal-
平均时间: 0 8133 0.20 0.00 0.00 0.00 0.20 - mpstat
平均时间: 0 8153 100.00 0.00 0.00 0.00 100.00 - stress
平均时间: 1000 8231 0.00 0.20 0.00 0.00 0.20 - pidstat
从这里可以明显看到,stress进程的CPU使用率为100%
首先开始运行stress命令,但这次模拟IO压力,即不停的执行sync:
$ stress -i 1 --timeout 600
stress: info: [8830] dispatching hogs: 0 cpu, 1 io, 0 vm, 0 hdd
还是在第二个终端指针uptime查看平均负载的变化情况:
$ watch -d uptime
load average: 1.06, 0.90, 0.64
然后在第三个终端运行upstat查看CPU使用率变化情况:
# -P ALL 表示监控所有CPU,后⾯数字5表示间隔5秒后输出⼀组数据
$ mpstat -P ALL 5
19时38分29秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
19时38分34秒 all 1.30 0.00 24.96 0.00 0.00 0.00 0.00 0.00 0.00 73.73
19时38分34秒 0 0.00 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 99.80
19时38分34秒 1 0.81 0.00 0.81 0.00 0.00 0.00 0.00 0.00 0.00 98.39
19时38分34秒 2 3.41 0.00 96.39 0.00 0.00 0.00 0.00 0.00 0.00 0.20
19时38分34秒 3 1.00 0.00 2.40 0.00 0.00 0.00 0.00 0.00 0.00 96.61
从这⾥可以看到,1 分钟的平均负载会慢慢增加到 1.06,其中⼀个 CPU 的系统CPU使⽤率升⾼到了 96.39 ,疑惑:为什么这里的iowait还是0,感觉这次CPU的升高是由于系统调用引起的????????
回答:
- iowait无法升高的问题:是因为stress使用的是sync()系统调用,它的作用是刷新缓冲区内存到磁盘中。对于新安装的虚拟机,缓冲区可能比较小,无法产生大的IO压力,这样大部分就是系统调用的消耗了,所以你会看到只有系统CPU使用率升高。
- 解决方法:使用stress的下一代stress-ng,它支持更丰富的选项,比如stress-ng -i --hdd 1 --tiimeout 600 (–hdd表示读写临时文件)
看一下到底是哪个进程导致了CPU使用率急剧上升:
# 间隔5s后输出一组数据,-u表示CPU指标
$ pidstat -u 5 1
Linux 5.8.0-48-generic (oceanstar) 2021年10月21日 _x86_64_ (4 CPU)
19时41分28秒 UID PID %usr %system %guest %wait %CPU CPU Command
19时41分33秒 0 679 0.20 0.00 0.00 0.00 0.20 3 vmtoolsd
19时41分33秒 1000 2187 0.00 0.40 0.00 0.00 0.40 0 Xorg
19时41分33秒 1000 2318 0.40 0.40 0.00 0.40 0.80 1 gnome-shell
19时41分33秒 1000 2470 0.00 0.20 0.00 0.00 0.20 3 vmtoolsd
19时41分33秒 1000 2659 0.00 0.20 0.00 0.20 0.20 0 gnome-terminal-
19时41分33秒 0 2898 0.00 0.20 0.00 0.00 0.20 2 kworker/2:1-events
19时41分33秒 0 8831 3.59 96.21 0.00 0.00 99.80 2 stress
19时41分33秒 1000 9422 0.00 0.20 0.00 0.00 0.20 1 pidstat
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 679 0.20 0.00 0.00 0.00 0.20 - vmtoolsd
平均时间: 1000 2187 0.00 0.40 0.00 0.00 0.40 - Xorg
平均时间: 1000 2318 0.40 0.40 0.00 0.40 0.80 - gnome-shell
平均时间: 1000 2470 0.00 0.20 0.00 0.00 0.20 - vmtoolsd
平均时间: 1000 2659 0.00 0.20 0.00 0.20 0.20 - gnome-terminal-
平均时间: 0 2898 0.00 0.20 0.00 0.00 0.20 - kworker/2:1-events
平均时间: 0 8831 3.59 96.21 0.00 0.00 99.80 - stress
平均时间: 1000 9422 0.00 0.20 0.00 0.00 0.20 - pidstat
从上面可以看到,还是因为stress
当系统中运行进程超出CPU运行能力时,就会出现等待CPU的进程。
首先开始运行stress命令,但这次模拟了16个进程:
$ stress -c 16 --timeout 600
stress: info: [10197] dispatching hogs: 16 cpu, 0 io, 0 vm, 0 hdd
由于系统只有4个CPU,明显比16个进程要少得多。因此,系统的CPU处于严重过载的状态(6.67):
$ uptime
19:50:26 up 1:47, 1 user, load average: 6.67, 2.08, 1.13
# mpstat -P ALL 5
Linux 5.8.0-48-generic (oceanstar) 2021年10月21日 _x86_64_ (4 CPU)
19时51分03秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
19时51分08秒 all 98.88 0.00 1.02 0.00 0.00 0.10 0.00 0.00 0.00 0.00
19时51分08秒 0 98.38 0.00 1.62 0.00 0.00 0.00 0.00 0.00 0.00 0.00
19时51分08秒 1 98.98 0.00 0.82 0.00 0.00 0.20 0.00 0.00 0.00 0.00
19时51分08秒 2 99.19 0.00 0.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00
19时51分08秒 3 98.99 0.00 0.81 0.00 0.00 0.20 0.00 0.00 0.00 0.00
接在运行pidstat查看进程的情况:
# 间隔5s后输出一组数据,-u表示CPU指标
$ pidstat -u 5 1
Linux 5.8.0-48-generic (oceanstar) 2021年10月21日 _x86_64_ (4 CPU)
19时51分46秒 UID PID %usr %system %guest %wait %CPU CPU Command
19时51分51秒 0 11 0.00 0.20 0.00 0.00 0.20 0 rcu_sched
19时51分51秒 0 679 0.20 0.00 0.00 0.00 0.20 1 vmtoolsd
19时51分51秒 1000 2187 0.80 4.38 0.00 0.60 5.18 3 Xorg
19时51分51秒 1000 2318 0.40 0.80 0.00 1.99 1.20 3 gnome-shell
19时51分51秒 1000 2659 0.60 3.59 0.00 0.00 4.18 0 gnome-terminal-
19时51分51秒 0 10198 23.90 0.00 0.00 75.90 23.90 1 stress
19时51分51秒 0 10199 23.90 0.20 0.00 76.10 24.10 0 stress
19时51分51秒 0 10200 23.51 0.40 0.00 75.70 23.90 0 stress
19时51分51秒 0 10201 23.51 0.20 0.00 75.90 23.71 2 stress
19时51分51秒 0 10202 25.30 0.40 0.00 74.30 25.70 3 stress
19时51分51秒 0 10203 31.08 0.20 0.00 68.33 31.27 1 stress
19时51分51秒 0 10204 22.71 0.00 0.00 76.69 22.71 1 stress
19时51分51秒 0 10205 23.11 0.80 0.00 76.10 23.90 0 stress
19时51分51秒 0 10206 28.49 0.40 0.00 70.72 28.88 3 stress
19时51分51秒 0 10207 22.31 0.20 0.00 77.49 22.51 1 stress
19时51分51秒 0 10208 22.51 0.40 0.00 77.49 22.91 3 stress
19时51分51秒 0 10209 23.90 0.20 0.00 76.10 24.10 3 stress
19时51分51秒 0 10210 22.71 0.40 0.00 76.89 23.11 2 stress
19时51分51秒 0 10211 22.31 0.00 0.00 77.49 22.31 2 stress
19时51分51秒 0 10212 22.11 0.40 0.00 77.29 22.51 0 stress
19时51分51秒 0 10213 22.71 0.00 0.00 77.29 22.71 2 stress
19时51分51秒 1000 10219 0.00 0.20 0.00 0.00 0.20 1 pidstat
可以看出,16个进程在争抢4个CPU,每个进程等待CPU的时间(%wait列)高达75%。这些超出CPU计算能力的进程,最终导致CPU过载
pidstat输出中没有%wait的问题:
- 因为centos默认的sysstat稍微有点老,源码或者RPM升级到11.5.5版本之后就可以看到了。
- 而unbuntu中的包一般都比较新,没有这个问题
mpstat无法观测的问题:
- 案例中是等待5s后输出1次结果就停止了,更好的做法是持续监控一段时间,比如持续观测20次: mpstat -P ALL 5 20
平均负载提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但只看平均负载本身,我们并不能直接发现,到底是哪里出了问题。所以,在理解平均负载时,也要注意:
语法:
mpstat [-P {cpu|ALL}] [internal [count]]
其中,各参数含义如下:
参数 | 含义 |
---|---|
-P {cpu l ALL} | 表示监控哪个CPU, cpu在[0,cpu个数-1]中取值 |
internal | 相邻的两次采样的间隔时间 |
count | 采样的次数,count只能和delay一起使用 |
输出参数含义
参数 | 释义 | 从/proc/stat获得数据 |
---|---|---|
CPU | 处理器ID | |
%usr | 在internal时间段里,用户态的CPU时间(%),不包含 nice值为负进程 | usr/total*100 |
%nice | 在internal时间段里,nice值为负进程的CPU时间(%) | nice/total*100 |
%sys | 在internal时间段里,核心时间(%) | system/total*100 |
%iowait | 在internal时间段里,硬盘IO等待时间(%) | iowait/total*100 |
%irq | 在internal时间段里,硬中断时间(%) | irq/total*100 |
%soft | 在internal时间段里,软中断时间(%) | softirq/total*100 |
%steal | 显示虚拟机管理器在服务另一个虚拟处理器时虚拟CPU处在非自愿等待下花费时间的百分比 | steal/total*100 |
%guest | 显示运行虚拟处理器时CPU花费时间的百分比 | guest/total*100 |
%gnice | gnice/total*100 | |
%idle | 在internal时间段里,CPU除去等待磁盘IO操作外的因为任何原因而空闲的时间闲置时间(%) | idle/total*100 |
CPU总的工作时间:
total_cur = user + system + nice + idle + iowait + irq + softirq
total_pre = pre_user + pre_system + pre_nice + pre_idle + pre_iowait + pre_irq + pre_softirq
user = user_cur – user_pre
total = total_cur - total_pre
其中_cur 表示当前值,_pre表示interval时间前的值。上表中的所有值可取到两位小数点。
实例
直接使用mpstat命令:
mpstat #当mpstat不带参数时,输出为从系统启动以来的平均值。
mpstat -P ALL 5 2 #表示每5秒产生一个报告,总共产生2个
上图表示每5秒产生了2个关于处理器的统计数据报告,一共产生2个interval 的信息,然后再给出这2个interval的平均信息。默认时,输出是按照CPU 号排序。第一个行给出了从系统引导以来的所有活跃数据。接下来每行对应一个处理器的活跃状态。