本文是通过学习倪朋飞老师的《Linux性能优化实战》 :到底应该怎么理解“平均负载”
平均负载?对很多人来说,可能既熟悉又陌生,我们每天的工作中,也都会提到这个词,但是我们真正理解它背后的含义吗?如果有人问我们什么是平均负载,我们能给他讲清楚什么是平均负载吗?
我曾做过总结,发现自己对平均负载的理解并不是很深刻,只知道只言片语,很是奇怪,随后我又和我们公司的运维讨论过,发现彼此都是一样,平时发现系统变慢了在命令行里输入top或者uptime命令进行查看,明明总能看到也总会用到,但是就是说不明白,其实还是自己的功底不够,理解的不够深刻。
[root@service home]# uptime
10:20:27 up 93 days, 14:52, 3 users, load average: 0.38, 0.40, 0.37
#10:20:27 当前时间
# up 93 days, 14:52 系统运行时间
# 3 users 正在登录用户数
# load average: 0.18, 0.27, 0.32 依次则是过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average)
于是,近几年,遇到问题,特别是基础问题,都会多查,多看,以理解现象背后的本质原理,用起来更灵活,也更有底气。
简单来说,平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。
- 可运行进程
所谓可运行状态的进程,是指正在使用 CPU 或者正在等待 CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态(Running 或 Runnable)的进程。 - 不可中断状态的进程
不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的, 比如最常见的是等待硬件设备的 I/O 响应,也就是我们在 ps 命令中看到的 D 状态 (Uninterruptible Sleep,也称为 Disk Sleep)的进程。
比如,当一个进程向MySql写数据时,为了保证数据的一致性,在得到MySql回复前,它是不能被其他进程中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现MySql数据与进程数据不一致的问题。
所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。
因此,可以简单理解为,平均负载其实就是平均活跃进程数。平均活跃进程数,直观上的理解就是单位时间内的活跃进程数,但它实际上是活跃进程数的指数衰减平均值。
既然平均的是活跃进程数,那么最理想的,就是每个 CPU 上都刚好运行着一个进程,这样每个CPU都得到了充分利用。比如当平均负载为 2 时,意味着什么呢?
- 在只有 2 个 CPU 的系统上,意味着所有的 CPU 都刚好被完全占用。
- 在 4 个 CPU 的系统上,意味着 CPU 有 50% 的空闲。
- 而在只有 1 个 CPU 的系统中,则意味着有一半的进程竞争不到 CPU。
平均负载为多少时合理
平均负载最理想的情况是等于 CPU个数。所以在评判平均负载时,首先要知道系统有几个 CPU,这可以通过top命令或者从文件 /proc/cpuinfo 中读取
[root@service ~]# cat /proc/cpuinfo |grep "model name"|wc -l
4
有了 CPU 个数,我们就可以判断出,当平均负载比 CPU 个数还大的时候,系统已经出现了过载。不过这个观察也是要评估多个指标的,我们要同时用1分钟5分钟15分钟的值来大概的估算这个设备的负载是呈现上升或者下降的趋势,其实最好的方式是通过监控的手段,采集足够多的load值,这样可以更精准的知道自己设备的负载情况。
当平均负载高于 CPU 数量 70% 的时候,你就应该分析排查负载高的问题了。 一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能。
但 70% 这个数字并不是绝对的,最推荐的方法,还是把系统的平均负载监控起来,然后 根据更多的历史数据,判断负载的变化趋势。当发现负载有明显升高趋势时,比如说负载翻倍了,再去做分析和调查。
平均负载与 CPU 使用率
在日常工作中,我们经常容易把平均负载和CPU使用率混淆,这里我们做下区分:
可能我们会疑惑,既然平均负载代表的是活跃进程数,那平均负载高了,不就意味着 CPU 使用率高吗?
我们还是要回到平均负载的含义上来,平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。所以,它不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O 的进程。
而 CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:
- CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
- I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
- 大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。
平均负载案例分析
这里会用到2个工具,stress和sysstat
stress是一个Linux系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景。
sysstat是一个linux性能工具,用来监控和分析系统的性能,以下案例中会用到这个包的2个命令mpstat和pidstat。
- mpstat 是一个常用的多核CPU性能分析工具用来实时查看每个CPU的性能指标,一级所有CPI的平均指标。
参数 | 释义 |
---|---|
CPU | 处理器ID |
%usr | 在internal时间段里,用户态的CPU时间(%),不包含 nice值为负进程 sr/total*100 |
%nice | 在internal时间段里,nice值为负进程的CPU时间(%) nice/total*100 |
%sys | 在internal时间段里,CPU时间(%) 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 |
- pidstat 是一个常用的进程性能分析工具,用来实时查看进程的CPU、内存、I/O以及上下文切换等性能指标。
参数 | 释义 |
---|---|
UID | 用户id |
PID | 进程ID |
%usr | 进程在用户空间占用cpu的百分比 |
%system | 进程在内核空间占用cpu的百分比 |
%guest | 进程在虚拟机占用cpu的百分比 |
%wait | 进程在等待CPU的时间百分比 |
%CPU | 进程占用cpu的百分比 |
CPU | 处理进程的cpu编号 |
%Command | 当前进程对应的命令 |
此外,每个场景都需要你开三个终端,登录到同一台 Linux 机器中。
场景一:CPU 密集型进程
我们在第一个终端运行 stress 命令,模拟一个 CPU 使用率 100% 的场景:
stress --cpu 2 --timeout 600
在第二个终端运行 uptime 查看平均负载的变化情况:
# -d 参数表示高亮显示变化的区域
watch -d uptime
11:01:49 up 93 days, 15:33, 2 users, load average: 1.90, 1.14, 0.66
# 在使用top看一下负载
top
top - 11:02:13 up 93 days, 15:34, 4 users, load average: 2.18, 1.66, 1.21
Tasks: 190 total, 4 running, 186 sleeping, 0 stopped, 0 zombie
%Cpu0 : 8.4 us, 16.1 sy, 0.0 ni, 74.6 id, 0.0 wa, 0.7 hi, 0.3 si, 0.0 st
%Cpu1 : 99.7 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.3 hi, 0.0 si, 0.0 st
%Cpu2 : 2.8 us, 7.3 sy, 0.0 ni, 77.1 id, 0.0 wa, 3.5 hi, 9.4 si, 0.0 st
%Cpu3 : 99.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.7 hi, 0.3 si, 0.0 st
MiB Mem : 3780.5 total, 144.2 free, 1227.9 used, 2408.4 buff/cache
MiB Swap: 4096.0 total, 4089.7 free, 6.3 used. 2290.5 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2181 root 20 0 7948 100 0 R 100.0 0.0 3:19.39 stress
2180 root 20 0 7948 100 0 R 99.7 0.0 3:19.23 stress
在第三个终端运行 mpstat 查看 CPU 使用率的变化情况:
# -P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据
mpstat -P ALL 5
Linux 4.18.0-147.5.1.el8_1.x86_64 (service) 2021年06月17日 _x86_64_ (4 CPU)
11时02分41秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
11时02分46秒 all 3.34 0.00 1.41 69.68 0.10 0.05 0.00 0.00 0.00 25.42
11时02分46秒 0 8.54 0.00 7.72 31.30 0.00 0.00 0.00 0.00 0.00 52.44
11时02分46秒 1 0.20 0.00 0.40 31.06 0.20 0.00 0.00 0.00 0.00 68.14
11时02分46秒 2 1.61 0.00 0.81 71.57 0.20 0.00 0.00 0.00 0.00 25.81
11时02分46秒 3 1.81 0.00 2.62 90.14 0.00 0.20 0.00 0.00 0.00 5.23
从终端二中可以看到,1 分钟的平均负载会慢慢增加到 2.08,而从终端三中还可以看到, 正好有2个 CPU 的使用率为 7.72%,而 iowait 高达 90.14%。这说明,平均负载的升高是由于 iowait 的升高 。
那么,到底是哪个进程导致了 CPU 使用率为 99.60% 呢?我们可以使用 pidstat 来查询:
# 间隔 5 秒后输出一组数据
pidstat -u 5 1
Linux 4.18.0-147.5.1.el8_1.x86_64 (webim-service-mq) 2021年06月17日 _x86_64_ (4 CPU)
11时03分29秒 UID PID %usr %system %guest %wait %CPU CPU Command
11时03分34秒 0 2180 99.40 0.00 0.00 0.00 99.40 3 stress
11时03分34秒 0 2181 99.60 0.00 0.00 0.00 99.60 1 stress
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 2180 99.40 0.00 0.00 0.00 99.40 - stress
平均时间: 0 2181 99.60 0.00 0.00 0.00 99.60 - stress
从这里可以明显看到,stress 进程的 CPU 使用率为 99.40%和99.60%。
场景二:I/O 密集型进程
首先还是运行 stress 命令,但这次模拟 I/O 压力,即不停地执行 sync:
#产生 1 个进程,每个进程都反复调用 sync 函数将内存上的内容写到硬盘上:
stress -i 1 --hdd 1 --timeout 3600
#--hdd表示读写临时文件
还是在第二个终端运行 uptime 查看平均负载的变化情况:
watch -d uptime
15:35:35 up 93 days, 20:07, 5 users, load average: 158.98, 269.72, 140.74
第三个终端运行 mpstat 查看 CPU 使用率的变化情况:
[root@webim-service-mq ~]# mpstat -P ALL 5 1
Linux 4.18.0-147.5.1.el8_1.x86_64 (webim-service-mq) 2021年06月17日 _x86_64_ (4 CPU)
15时35分46秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
15时35分51秒 all 2.85 0.00 6.84 43.32 1.04 2.18 0.00 0.00 0.00 43.78
15时35分51秒 0 2.52 0.00 5.67 50.21 0.63 0.00 0.00 0.00 0.00 40.97
15时35分51秒 1 4.12 0.00 8.25 22.89 0.82 0.21 0.00 0.00 0.00 63.71
15时35分51秒 2 1.67 0.00 4.60 70.92 1.67 8.58 0.00 0.00 0.00 12.55
15时35分51秒 3 3.05 0.00 8.76 29.94 1.02 0.00 0.00 0.00 0.00 57.23
从这里可以看到,1 分钟的平均负载会慢慢增加到 14.92,其中2个 CPU 的系统 CPU 使用率升高到了 10%以上,而 iowait 高达70%以上,这就说明,平均负载的升高是由于iowait的升高、。
那么到底是哪个进程,导致 iowait 升高呢?我们还是用 pidstat 来查询:
pidstat -u 5 1
11时48分27秒 UID PID %usr %system %guest %wait %CPU CPU Command
11时48分32秒 0 3598 0.20 0.00 0.00 0.40 0.20 1 watch
11时48分32秒 0 14161 0.00 2.00 0.00 0.00 2.00 1 stress
11时48分32秒 0 14162 0.00 7.20 0.00 0.20 7.20 2 stress
可以发现,还是 stress 进程导致的。
场景三:大量进程的场景
当系统中运行进程超出 CPU 运行能力时,就会出现等待 CPU 的进程
我们还是使用 stress,但这次模拟的是 80 个进程:
stress -c 80 --timeout 3600
在第二个终端运行 uptime 查看平均负载的变化情况:
watch -d uptime
14:56:21 up 93 days, 19:28, 5 users, load average: 70.75, 82.49, 61.12
使用 pidstat 来看一下进程的情况
pidstat -u 5 1
14时55分01秒 0 9657 4.58 0.00 0.00 94.62 4.58 1 stress
14时55分01秒 0 9658 4.78 0.00 0.00 95.22 4.78 3 stress
14时55分01秒 0 9659 4.78 0.20 0.00 95.82 4.98 2 stress
14时55分01秒 0 9660 4.78 0.00 0.00 92.83 4.78 2 stress
14时55分01秒 0 9661 4.78 0.00 0.00 97.21 4.78 1 stress
14时55分01秒 0 9662 4.58 0.00 0.00 92.63 4.58 0 stress
14时55分01秒 0 9663 4.78 0.00 0.00 93.23 4.78 1 stress
14时55分01秒 0 9664 5.18 0.00 0.00 98.01 5.18 2 stress
14时55分01秒 0 9665 4.98 0.00 0.00 97.41 4.98 2 stress
14时55分01秒 0 9666 4.78 0.00 0.00 96.02 4.78 3 stress
14时55分01秒 0 9667 4.78 0.00 0.00 96.41 4.78 3 stress
14时55分01秒 0 9668 4.78 0.20 0.00 92.43 4.98 2 stress
14时55分01秒 0 9669 4.78 0.00 0.00 91.24 4.78 2 stress
14时55分01秒 0 9670 4.78 0.00 0.00 96.22 4.78 3 stress
14时55分01秒 0 9671 4.78 0.20 0.00 95.42 4.98 2 stress
14时55分01秒 0 9672 4.78 0.00 0.00 92.43 4.78 2 stress
14时55分01秒 0 9673 4.58 0.00 0.00 93.82 4.58 1 stress
14时55分01秒 0 9674 4.78 0.00 0.00 94.82 4.78 3 stress
14时55分01秒 0 9675 4.58 0.00 0.00 91.63 4.58 1 stress
14时55分01秒 0 9676 4.78 0.00 0.00 102.59 4.78 3 stress
14时55分01秒 0 9677 4.58 0.00 0.00 97.41 4.58 1 stress
14时55分01秒 0 9678 4.58 0.00 0.00 91.83 4.58 2 stress
14时55分01秒 0 9679 4.78 0.00 0.00 96.61 4.78 1 stress
可以看出,80个进程在争抢 4个 CPU,每个进程等待 CPU 的时间(也就是代码块中的 %wait 列)高达 90%以上。这些超出 CPU 计算能力的进程,最终导致 CPU 过载。
总结:
平均负载提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但只看平均负载本身,我们并不能直接发现,到底是哪里出现了瓶颈。所以,在理解平均负载时,也要注意:
- 平均负载高有可能是 CPU 密集型进程导致的;
- 平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了;
- 当发现负载高的时候,你可以使用 mpstat、pidstat 等工具,辅助分析负载的来源。