当我们发现系统变慢时,通常会执行top
或者uptime
命令,来了解系统的负载情况,比如:
1 | $ uptime |
uptime
命令输出的信息分别是:当前系统时间,系统运行时间,当前登陆用户数,以及最近1分钟、最近5分钟和最近15分钟的系统评价负载情况
什么是系统平均负载?
我们可以通过man uptime
查看命令帮助手册,有这么一段话介绍什么是系统的平均负载:
System load averages is the average number of processes that are either in a runnable or uninterruptable state. A process in a runnable state is either using the CPU or waiting to use the CPU. A process in uninterruptable state is waiting for some I/O access, eg waiting for disk. The averages are taken over the three time intervals. Load averages are not normalized for the number of CPUs in a system, so a load average of 1 means a single CPU system is loaded all the time while on a 4 CPU system it means it was idle 75% of the time.
简单来说,平均负载就是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数。这里的可运行状态包括正在使用CPU(Running状态)和正在等待CPU(Runnable状态)。而不可中断状态是指进程运行在内核态,正在等待I/O
,为了能够尽快完成I/O
操作,等待I/O
的进程不会被中断。
通过ps
命令查看进程时,可运行状态对应的是R
,而不可中断状态对应的是D
。
系统平均负载不会根据 CPU 数量进行标准化,也就是说,当系统平均负载是1的时候:
- 在单核系统上,说明 CPU 满跑
- 而在4核系统上,说明 CPU 有75%的空闲
平均负载为多少合适?
理想的情况下,是每个 CPU 都刚好运行着一个进程,这样每个 CPU 都能得到充分的利用,因此理想情况下平均负载应该等于系统 CPU 的个数。
当我们在评判系统负载情况的时候,应该先要知道系统有几个CPU:
1 | $ grep 'model name' /proc/cpuinfo| wc -l |
/proc/cpuinfo
文件内会记录系统 CPU 的信息,我们可以直接通过统计该文件信息获取 CPU 个数。
通过与 CPU 个数对比,我们就可以知道当前系统的负载情况是否过载。
平均负载的值有三个,分别对应最近1分钟,最近5分钟和最近15分钟,我们可以根据这三个值来更全面的了解系统的负载情况:
- 如果这三个值基本相差不大,那就说明系统负载比较平稳
- 如果最近1分钟的负载远小于15分钟的负载,说明过去15分钟负载很大,而现在在慢慢减小
- 如果最近1分钟负载很大,而15分钟的很小,说明最近1分钟的负载在逐渐增加,这种增加可能是临时性的,也有可能会持续增加,需要持续观察
在生产环境中,当平均负载高于CPU数量的70%时,就应该分析排查负载高的问题了。一旦负载过高,就会导致进程响应变慢,进而影响服务正常功能。
平均负载与CPU使用率
CPU使用率是单位时间内对CPU繁忙情况的统计。根据平均负载的含义,我们知道这两者是两个不同的概念。
平均负载与CPU使用率两者之间也不一定是对应的:
- CPU密集型进程:会使用大量的CPU,这时候两者一般是一致的
- IO密集型进程:等待IO会导致平均负载升高,但是这时候进程是处于等待状态的,因此这时候的CPU使用率不一定高
- 大量进程等待CPU调度:这时候的平均负载会很高,而CPU使用率一般也会比较高
当我们发现系统的平均负载很高时,首先就需要分析是由于上面哪种原因导致的,这样才能有效的解决问题。
案例模拟分析
接下来我们来模拟一下平均负载过高的案例分析。
首先要先安装一下stress-ng
和sysstat
两个包。其中stress-ng
是压力测试工具,用来模拟平均负载过高的场景,而sysstat
包含了常用的性能工具,用来监控和分析系统的性能,包括:
mpstat
:多核cpu性能工具,用来实时查看每个cpu的性能指标,以及所有cpu的平均指标pidstat
:进程性能分析工具,用来实时查看进程的cpu、内存、i/o以及上下文切换等性能指标
场景一:CPU密集型进程
首先,在第一个终端运行:
1 | $ stress-ng --cpu 2 --timeout 600 |
-- cpu 2
:使用2个worker去不断执行cpu密集的任务,这里使用2是因为我本地测试的虚拟机只有两个cpu--timeout 600
:表示持续运行600s
接着在第二个终端运行:
1 | $ watch -d uptime # -d 会高亮显示变化的区域 |
我们可以在这个终端看到,平均负载会逐渐升高,最后最近1分钟的平均负载会稳定在2左右
最后,在第三个终端运行:
1 | $ mpstat -P ALL 5 20 # -P All 表示统计所有cpu信息,5表示间隔5s打印一次统计,20表示共打印20组统计 |
我们可以看到,两个cpu的用户使用率都接近100%,说明是因为cpu密集进程导致的平均负载过高
接下来,我们可以使用pidstat
命令来查询异常进程:
1 | $ pidstat -u 5 2 # -u输出cpu使用率,间隔5s打印一次统计信息,共打印2组 |
从上面可以看到,大量占用cpu使用率的进程是stress-ng
命令
场景二:io密集型进程
首先在第一个终端执行命令:
1 | $ stress-ng --hdd 2 --timeout 600 |
--hdd 2
:表示使用两个worker
不断的读写临时文件--timeout
:表示持续运行600s
接着,在第二个终端运行:
1 | $ watch -d uptime |
可以看到,平均负载不断上升,最终文档在2.7~2.8之间
最后,在第三个终端运行:
1 | $ mpstat -P ALL 5 1 |
%iowait: the percentage of time that the CPU or CPUs were idle during which the system had an outstanding disk I/O request.
%idle: the percentage of time that the CPU or CPUs were idle and the system did not have an outstanding disk I/O request.
从输出结果我们可以看到,尽管系统的平均负载很高,但是cpu使用率并不高,而iowait却很高,可以判断是由于io密集导致的系统负载过高。
接下来,我们使用pidstat
来查找出导致过载的进程:
1 | $ pidstat -d # -d输出io统计信息 |
场景三:大量进程场景
首先在第一个终端运行:
1 | $ stress-ng -c 8 --timeout 600 # -c 8 表示开启8个进程 |
然后在第二个终端运行:
1 | $ watch -d uptime |
可以观察到,经过一段时间之后,系统负载上升到8.0左右,远远高于正常系统负载,我们使用pidstat
看一下进程情况:
1 | $ pidstat -u 3 1 |
我们看到有8个进程在争夺系统的两个cpu,平均每个进程占用25%的cpu使用率。
总结
平均负载提供了一个快速查看系统整体性能的手段,反应了系统整体的负载情况,但是只看平均负载本身并不能直接发现系统瓶颈。
- 平均负载高有可能是cpu密集型进程导致的
- 平均负载高不一定代表cpu使用率高,有可能是I/O繁忙导致的
- 当发现平均负载过高时,可以使用
mpstat
和pidstat
等工具辅助分析