什么是平均负载
[root@111 ~]# uptime 11:03:33 up 149 days, 17:34, 1 user, load average: 0.08, 0.05, 0.01
最后三个数字,依次则是过去1分钟、5分钟、15分钟的平均负载(Load Average),常用的top工具也能展示
平均负载作为极其重要的系统指标,是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和CPU使用率并没有直接关系。
可运行状态的进程,是指正在使用CPU或者正在等待CPU的进程,也就是我们常用ps命令看到的,处于R状态(Running 或 Runnable)的进程。
不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的I/O响应,也就是我们在ps命令中看到的D状态(Uninterruptible Sleep)的进程。
比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题。
所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。
综上所述,平均负载其实就是平均活跃进程数,即单位时间内的活跃进程数。
平均负载为多少时合理
最理想的状态,是每个CPU上都刚好跑着一个进程,这样每个CPU都得到了充分利用。比如当平均负载为2时,意味着什么呢?
-
在只有2个CPU的系统上,意味着所有的CPU都刚好被完全占用。
-
在4个CPU的系统上,意味着CPU有50%的空闲。
-
而在只有1个CPU的系统中,则意味着有一半的进程竞争不到CPU。
所以,平均负载最理想的情况是等于 CPU个数。在评判平均负载时,首先要知道系统有几个 CPU,可以通过 top 命令或者从文件 /proc/cpuinfo 中读取,比如:
逻辑cpu个数: [root@111 ~]# grep processor /proc/cpuinfo |wc -l 4
一般来说,实际生产环境中,当平均负载为 CPU 数量的70-80%的时候,就应该分析排查负载高的问题了。
另一方面,我们需要关注平均负载三个数值的变化:
-
如果1分钟、5分钟、15分钟的三个值基本相同,或者相差不大,那就说明系统负载很平稳。
-
但如果1分钟的值远小于15 分钟的值,就说明系统最近1分钟的负载在减少,而过去15分钟内却有很大的负载。
-
反过来,如果1分钟的值远大于 15 分钟的值,就说明最近1分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,所以就需要持续观察。一旦1分钟的平均负载接近或超过了CPU的个数,就意味着系统正在发生过载的问题,这时就得分析调查是哪里导致的问题,并要想办法优化了。
举个栗子,4核的机子,平均负载为 3.23,1.60,6.98:
- 在过去的15分钟,平均负载超过cpu个数,说明负载很高,可能是cpu使用率很高,也可能是I/O的压力等等原因。
- 过去1分钟和过去5分钟,平均负载都小于cpu个数,说明负载正常,从整体趋势来看,系统的负载在降低,但是过去1分钟的值大于过去5分钟的值,说明平均负载又在升高。
70-80%这个数字并不是绝对的,毕竟最长的15分钟的数据也只是短期的表现,最推荐的方法,是把系统的平均负载监控起来,然后根据更多的历史数据,判断负载的整体变化趋势。
平均负载与CPU使用率
现实工作中,我们经常容易把平均负载和 CPU 使用率混淆,其实两者还是有区别的。
可能你会疑惑,既然平均负载代表的是活跃进程数,那平均负载高了,不就意味着 CPU 使用率高吗?
我们还是要回到平均负载的含义上来,平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。所以,它不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O 的进程。
CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:
-
CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
-
I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
-
大量等待 CPU 的进程调度也会导致平均负载升高,此时的CPU使用率也会比较高。
平均负载案例模拟分析
模拟三个场景分别来看以上三种情况,使用iostat、mpstat、pidstat 等工具,找出平均负载升高的根源。
- 机器配置:4cpu,16G内存,centos
- 安装 stress 和 sysstat 工具包,yum install stress sysstat -y
stress 是一个 Linux 系统压力测试工具
stress语法格式: stress <options> 常用选项: -c, --cpu N 产生 N 个进程,每个进程都反复不停的计算随机数的平方根 -i, --io N 产生 N 个进程,每个进程反复调用 sync() 将内存上的内容写到硬盘上 -m, --vm N 产生 N 个进程,每个进程不断分配和释放内存 --vm-bytes B 指定分配内存的大小 --vm-stride B 不断的给部分内存赋值,让 COW(Copy On Write)发生 --vm-hang N 指示每个消耗内存的进程在分配到内存后转入睡眠状态 N 秒,然后释放内存,一直重复执行这个过程 --vm-keep 一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新分配内存) -d, --hadd N 产生 N 个不断执行 write 和 unlink 函数的进程(创建文件,写入内容,删除文件) --hadd-bytes B 指定文件大小 -t, --timeout N 在 N 秒后结束程序 --backoff N 等待N微妙后开始运行 -q, --quiet 程序在运行的过程中不输出信息 -n, --dry-run 输出程序会做什么而并不实际执行相关的操作 --version 显示版本号 -v, --verbose 显示详细的信息
sysstat 包含了常用的 Linux 性能工具,用来监控和分析系统的性能,主要包括:
iostat 工具提供CPU使用率及硬盘吞吐效率的数据; #比较核心的工具 mpstat 工具提供单个处理器或多个处理器相关数据; pidstat: 关于运行中的进程/任务、CPU、内存等的统计信息 sar 工具负责收集、报告并存储系统活跃的信息; #统计数据的核心工具 sa1 工具负责收集并存储每天系统动态信息到一个二进制的文件中。它是通过计划任务工具cron来运行,是为sadc所设计的程序前端程序; sa2工具负责把每天的系统活跃性息写入总结性的报告中。它是为sar所设计的前端 ,要通过cron来调用 sadc 是系统动态数据收集工具,收集的数据被写一个二进制的文件中,它被用作sar工具的后端; sadf 显示被sar通过多种格式收集的数据; nfsiostat: NFS(Network File System)的I/O统计信息。 cifsiostat: CIFS(Common Internet File System)的统计信息。
每个场景都需要开三个终端,登录到同一台 Linux 机器中,使用uptime看一下测试前的平均负载:
[root@123~]# uptime 16:11:04 up 150 days, 22:42, 3 users, load average: 0.06, 0.03, 0.00
场景一:CPU 密集型进程
首先,在第一个终端运行 stress 命令,模拟一个 CPU 使用率 100% 的场景:
#产生1个进程反复不停的计算随机数的平方根,300秒后停止 [root@123 ~]# stress -c 1 -t 300
接着,在第二个终端运行watch -d uptime查看平均负载的变化情况:
# -d 参数表示高亮显示变化的区域
Every 2.0s: uptime 16:26:55 up 150 days, 22:58, 3 users, load average: 1.05, 0.76, 0.40
可以看到,1 分钟的平均负载会慢慢增加到 1.00出头
最后,在第三个终端运行mpstat查看 CPU 使用率的变化情况:
mpstat --help 用法: mpstat [ 选项 ] [ <时间间隔> [ <次数> ] ] 选项: [ -I { SUM | CPU | SCPU | ALL } ] [ -N { <node_list> | ALL } ] [ --dec={ 0 | 1 | 2 } ] [ -o JSON ] [ -P { <CPU_列表> | ALL } ] #-P ALL 表示监控所有CPU,间隔5秒后输出一组数据,总计5组 [root@123~]# mpstat -P ALL 5 5 04:25:28 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 04:25:33 PM all 25.11 0.00 0.20 0.35 0.00 0.00 0.00 0.00 74.34 04:25:33 PM 0 0.40 0.00 0.20 0.00 0.00 0.00 0.00 0.00 99.40 04:25:33 PM 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 04:25:33 PM 2 0.00 0.00 0.60 1.60 0.00 0.00 0.00 0.00 97.80 04:25:33 PM 3 0.00 0.00 0.20 0.00 0.00 0.00 0.00 0.00 99.80 04:25:33 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 04:25:38 PM all 25.13 0.00 0.15 0.40 0.00 0.00 0.00 0.00 74.32 04:25:38 PM 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 04:25:38 PM 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 04:25:38 PM 2 0.20 0.00 0.40 1.41 0.00 0.00 0.00 0.00 97.99 04:25:38 PM 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
可以看到,正好有一个 CPU 的使用率为 100%,但它的 iowait 只有 0。这说明,平均负载的升高正是由于 CPU 使用率为 100%
那么,到底是哪个进程导致了 CPU 使用率为 100% 呢?可以使用 pidstat 来查询:
pidstat --help 用法:pidstat [ 选项 ] [ <时间间隔> [ <计数> ] ] [ -e <程序> <参数> ] 选项: [ -d ] [ -H ] [ -h ] [ -I ] [ -l ] [ -R ] [ -r ] [ -s ] [ -t ] [ -U [ <用户名> ] ] [ -u ] [ -V ] [ -v ] [ -w ] [ -C <命令> ] [ -G <进程名> ] [ -p { <pid> [,...] | SELF | ALL } ] [ -T { TASK | CHILD | ALL } ] [ --dec={ 0 | 1 | 2 } ] [ --human ] 常用选项解释: -u:默认的参数,显示各个进程的cpu使用统计 -r:显示各个进程的内存使用统计 -d:显示各个进程的IO使用情况 -p:指定进程号 -w:显示每个进程的上下文切换情况 #间隔5秒输出一组数据,总计5组 [root@123 ~]# pidstat -u 5 5 04:26:17 PM PID %usr %system %guest %CPU CPU Command 04:26:22 PM 2464 0.20 0.00 0.00 0.20 3 netdata 04:26:22 PM 2677 0.20 0.00 0.00 0.20 0 python 04:26:22 PM 26629 0.00 0.40 0.00 0.40 2 apps.plugin 04:26:22 PM 28735 100.00 0.00 0.00 100.00 3 stress 04:26:22 PM PID %usr %system %guest %CPU CPU Command 04:26:27 PM 2464 0.20 0.00 0.00 0.20 3 netdata 04:26:27 PM 26629 0.20 0.20 0.00 0.40 2 apps.plugin 04:26:27 PM 28735 100.00 0.00 0.00 100.00 3 stress
从这里可以明显看到,stress进程的CPU使用率为100%。
场景二:I/O 密集型进程
首先还是运行 stress 命令,但这次模拟 I/O 压力,即不停地执行 sync:
# 产生1个进程,反复调用 sync() 将内存上的内容写到硬盘上,300秒后结束 [root@123~]# stress -i 1 -t 300
还是在第二个终端运行watch -d uptime查看平均负载的变化情况:
17:14:02 up 150 days, 23:45, 4 users, load average: 1.52, 1.68, 0.80
可以看到,平均负载再次上升
然后,第三个终端运行mpstat查看 CPU 使用率的变化情况:
#显示所有CPU的指标,间隔5秒输出一组数据,持续输出 [root@123 ~]# mpstat -P ALL 5 05:08:10 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 05:08:15 PM all 0.15 0.00 0.15 0.20 0.00 0.00 0.00 0.00 99.50 05:08:15 PM 0 0.20 0.00 0.20 0.00 0.00 0.00 0.00 0.00 99.60 05:08:15 PM 1 0.20 0.00 0.40 0.00 0.00 0.00 0.00 0.00 99.40 05:08:15 PM 2 0.20 0.00 0.00 1.00 0.00 0.00 0.00 0.00 98.80 05:08:15 PM 3 0.20 0.00 0.20 0.00 0.00 0.00 0.00 0.00 99.60 05:08:15 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 05:08:20 PM all 0.15 0.00 0.20 10.67 0.00 0.00 0.00 0.00 88.98 05:08:20 PM 0 0.20 0.00 0.00 14.63 0.00 0.00 0.00 0.00 85.17 05:08:20 PM 1 0.00 0.00 0.40 0.00 0.00 0.00 0.00 0.00 99.60 05:08:20 PM 2 0.00 0.00 0.20 28.11 0.00 0.00 0.00 0.00 71.69 05:08:20 PM 3 0.20 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.80 05:08:20 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 05:08:25 PM all 0.15 0.00 0.20 22.19 0.00 0.00 0.00 0.00 77.45 05:08:25 PM 0 0.20 0.00 0.20 29.46 0.00 0.00 0.00 0.00 70.14 05:08:25 PM 1 0.20 0.00 0.40 0.00 0.00 0.00 0.00 0.00 99.40 05:08:25 PM 2 0.20 0.00 0.20 59.44 0.00 0.00 0.00 0.00 40.16 05:08:25 PM 3 0.20 0.00 0.20 0.00 0.00 0.00 0.00 0.00 99.60
可以发现,系统CPU使用率很低,而 iowait很高,其中一个高达 59.44%,这说明平均负载的升高是由于 iowait 的升高。
是哪个进程导致iowait这么高?
#pidstat -d:显示各个进程的IO使用情况
[root@123 ~]# pidstat -d 3 3 05:12:56 PM PID kB_rd/s kB_wr/s kB_ccwr/s Command 05:12:59 PM 701 0.00 68.00 0.00 jbd2/dm-0-8 05:12:59 PM 1518 0.00 292.00 0.00 flush-253:0 05:12:59 PM 1968 0.00 28.00 0.00 java 05:12:59 PM PID kB_rd/s kB_wr/s kB_ccwr/s Command 05:13:02 PM 701 0.00 80.00 0.00 jbd2/dm-0-8 05:13:02 PM 1518 0.00 268.00 0.00 flush-253:0 05:13:02 PM 1968 0.00 30.67 0.00 java 05:13:02 PM PID kB_rd/s kB_wr/s kB_ccwr/s Command 05:13:05 PM 701 0.00 80.00 0.00 jbd2/dm-0-8 05:13:05 PM 1518 0.00 286.67 0.00 flush-253:0 05:13:05 PM 1968 0.00 36.00 0.00 java
jbd2/dm-0-8和flush-253:0都是操作系统针对磁盘和文件系统的进程,本质上是stress 进程导致的,因为300秒后当stress结束了,这两个进程也结束了。
场景三:大量进程的场景
当系统中运行进程超出 CPU 运行能力时,就会出现等待 CPU 的进程。
比如,我们还是使用 stress,但这次模拟的是 8 个进程:
[root@123 ~]# stress -c 8 -t 300
由于系统只有 4 个CPU,比 8 个进程要少一半,因而,系统的 CPU 处于严重过载状态,平均负载高达7.90:
11:35:53 up 155 days, 18:07, 4 users, load average: 7.90, 5.51, 3.00
使用mpstat查看cpu使用率
[root@123~]# mpstat -P ALL 3 10 11:30:17 AM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 11:30:20 AM all 99.83 0.00 0.17 0.00 0.00 0.00 0.00 0.00 0.00 11:30:20 AM 0 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 11:30:20 AM 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 11:30:20 AM 2 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 11:30:20 AM 3 99.67 0.00 0.33 0.00 0.00 0.00 0.00 0.00 0.00 11:30:20 AM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 11:30:23 AM all 99.92 0.00 0.08 0.00 0.00 0.00 0.00 0.00 0.00 11:30:23 AM 0 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 11:30:23 AM 1 99.67 0.00 0.33 0.00 0.00 0.00 0.00 0.00 0.00 11:30:23 AM 2 99.67 0.00 0.33 0.00 0.00 0.00 0.00 0.00 0.00 11:30:23 AM 3 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
可见4个cpu都被打满,再用pidstat看一下进程使用cpu的情况:
[root@123 ~]# pidstat 3 10 11:30:20 AM PID %usr %system %guest %CPU CPU Command 11:30:23 AM 2385 0.33 0.00 0.00 0.33 2 java 11:30:23 AM 2464 0.33 0.33 0.00 0.66 0 netdata 11:30:23 AM 26384 49.67 0.00 0.00 49.67 1 stress 11:30:23 AM 26385 49.67 0.00 0.00 49.67 0 stress 11:30:23 AM 26386 49.01 0.00 0.00 49.01 3 stress 11:30:23 AM 26387 49.67 0.00 0.00 49.67 2 stress 11:30:23 AM 26388 50.66 0.00 0.00 50.66 3 stress 11:30:23 AM 26389 49.67 0.00 0.00 49.67 0 stress 11:30:23 AM 26390 49.67 0.00 0.00 49.67 1 stress 11:30:23 AM 26391 49.67 0.00 0.00 49.67 2 stress 11:30:23 AM PID %usr %system %guest %CPU CPU Command 11:30:26 AM 26223 0.33 0.00 0.00 0.33 3 watch 11:30:26 AM 26384 49.67 0.00 0.00 49.67 1 stress 11:30:26 AM 26385 50.00 0.00 0.00 50.00 0 stress 11:30:26 AM 26386 50.00 0.00 0.00 50.00 3 stress 11:30:26 AM 26387 50.00 0.00 0.00 50.00 2 stress 11:30:26 AM 26388 49.67 0.00 0.00 49.67 3 stress 11:30:26 AM 26389 50.00 0.00 0.00 50.00 0 stress 11:30:26 AM 26390 50.00 0.00 0.00 50.00 1 stress 11:30:26 AM 26391 50.00 0.00 0.00 50.00 2 stress 11:30:26 AM 26524 0.00 0.33 0.00 0.33 2 pidstat
可以看出,8 个stress进程在争抢 4 个 CPU,每个进程都占据着0.5个cpu,同时等待 CPU 的时间也是 50%。这些超出 CPU 计算能力的进程,最终导致 CPU 过载。
小结
分析完这三个案例,归纳一下平均负载的理解。
平均负载提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但只看平均负载本身,我们并不能直接发现,到底是哪里出现了瓶颈。所以,在理解平均负载时,也要注意:
-
平均负载高有可能是 CPU 密集型进程导致的;
-
平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了;
-
当发现负载高的时候,你可以使用 mpstat、pidstat 等工具,辅助分析负载的来源。