CPU性能之平均负载的理解

CPU性能之平均负载的理解

evobot 1,015 2020-09-10

什么是平均负载

  • uptime命令的输出中,前几列分别是当前时间,系统运行时间和当前登陆的用户数;

    $ uptime
    02:34:03 up 2 days, 20:14,  1 user,  load average: 0.63, 0.83, 0.88
    
  • 而后三列则是一分钟五分钟15分钟的平均负载,平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。

  • 可运行状态的进程,是指正在使用 CPU 或者正在等待 CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态(Running 或 Runnable)的进程。

  • 不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的 I/O 响应,也就是我们在 ps 命令中看到的 D 状态(Uninterruptible Sleep,也称为 Disk Sleep)的进程。比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。

平均负载的合理值

  • 平均负载最理想的情况是等于 CPU 个数。所以在评判平均负载时,首先要知道系统有几个 CPU,可以通过 top 命令或者从文件 /proc/cpuinfo 中读取:

    # 关于grep和wc的用法请查询它们的手册或者网络搜索
    $ grep 'model name' /proc/cpuinfo | wc -l
    2
    
  • 当平均负载比CPU个数大时,系统就出现了过载;而三个负载可以更加全方位的了解系统的负载情况:

    • 如果 1 分钟、5 分钟、15 分钟的三个值基本相同,或者相差不大,那就说明系统负载很平稳。
    • 如果 1 分钟的值远小于 15 分钟的值,就说明系统最近 1 分钟的负载在减少,而过去 15 分钟内却有很大的负载。
    • 反过来,如果 1 分钟的值远大于 15 分钟的值,就说明最近 1 分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,所以就需要持续观察。一旦 1 分钟的平均负载接近或超过了 CPU 的个数,就意味着系统正在发生过载的问题。
  • 在实际生产环境中,一般当平均负载高于CPU数量70%时,就应该对负载过高问题进行排查。

平均负载与CPU使用率

  • 平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。所以,它不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O 的进程。
  • CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:
    • CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
    • I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
    • 大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。

平均负载分析

  • 安装stress-ngsysstat软件包,centos由于默认源里的sysstat包比较老,建议直接下载sysstat-11.7.3的rpm包:

    $ yum install -y stress-ng
    $ wget http://www.rpmfind.net/linux/centos/8.1.1911/AppStream/x86_64/os/Packages/sysstat-11.7.3-2.el8.x86_64.rpm
    $ yum install sysstat-11.7.3-2.el8.x86_64.rpm
    
    • stress-ng 是一个 Linux 系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景。
    • sysstat 包含了常用的 Linux 性能工具,用来监控和分析系统的性能,这里主要使用其中的mpstatiostatpidstat
    • mpstat 是一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标;
    • pidstat 是一个常用的进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标。

案例一:CPU密集型进程

  • 在第一个终端中运行stress-ng命令,模拟CPU使用率100%的场景:

    $ stress-ng --cpu 1 --timeout 600
    stress-ng: info: [3150] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
    
  • 在第二个终端中使用uptime查看平均负载变化情况:

    # -d 参数表示高亮显示变化的区域
    $ watch -d uptime
    Every 2.0s: uptime                                                                                      Thu Sep 10 03:20:08 2020
    
    03:22:52 up 36 min,  3 users,  load average: 1.00, 0.61, 0.29
    
  • 在第三个终端中运行mpstat查看CPU使用率变化的情况:

    # -P ALL 表示监控所有CPU,后面数字5表示间隔5秒后输出一组数据
    $ mpstat -P ALL 5
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    05:09:25 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    05:09:30 AM  all   24.21    0.00    0.25    0.00    0.00    0.05    0.00    0.00    0.00   75.48
    05:09:30 AM    0    0.40    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00   99.20
    05:09:30 AM    1    0.20    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00   99.60
    05:09:30 AM    2    0.00    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00   99.60
    05:09:30 AM    3   99.79    0.00    0.00    0.00    0.00    0.21    0.00    0.00    0.00    0.00
    
  • 从终端2中可以看到,1分钟的负载会满满增加到1.00,而终端3中可以看到正好有一个CPU的使用率为100%,但它的iowait始终是0,这说明平均负载的升高是由于CPU的使用率为100%;

  • 然后使用pidstat命令来查看到底是哪个进程导致了CPU使用率为100%:

    # 间隔5秒后输出一组数据
    $ pidstat -u 5 1
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    05:11:36 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    05:11:41 AM     0      2055    0.20    0.20    0.00    0.00    0.40     0  java
    05:11:41 AM    27      2269    0.00    0.20    0.00    0.00    0.20     3  mysqld
    05:11:41 AM     0      8647   99.60    0.00    0.00    0.00   99.60     3  stress-ng-cpu
    05:11:41 AM     0      8657    0.20    0.40    0.00    0.00    0.60     1  watch
    05:11:41 AM     0      8821    0.20    0.20    0.00    0.00    0.40     1  pidstat
    

这里可以看到,stress-ng的进程的CPU使用率为100%。

案例二:I/O密集型进程

  • 使用stress-ng命令模拟I/O压力,即不停的执行sync:

    $ stress-ng -i1 --hdd 1 --timeout 600
    stress-ng: info: [5567] dispatching hogs: 0 cpu, 1 io, 0 vm, 0 hdd
    
  • 第二个终端还是运行uptime命令查看平均负载变化情况:

    $ watch -d uptime
    Every 2.0s: uptime                                                                                      Thu Sep 10 03:56:10 2020
    
     03:56:10 up  1:10,  3 users,  load average: 1.08, 0.44, 0.25
    
  • 第三个终端运行mpstat命令查看CPU使用率变化情况:

    # 显示所有CPU的指标,并在间隔5秒输出一组数据
    $ mpstat -P ALL 5 1
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    04:26:48 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    04:26:53 AM  all    0.66    0.00   21.50   39.33    0.00    2.03    0.00    0.00    0.00   36.48
    04:26:53 AM    0    0.62    0.00   28.42   65.98    0.00    0.21    0.00    0.00    0.00    4.77
    04:26:53 AM    1    0.48    0.00    5.54    3.13    0.00    4.82    0.00    0.00    0.00   86.02
    04:26:53 AM    2    0.65    0.00   24.57   57.83    0.00    2.83    0.00    0.00    0.00   14.13
    04:26:53 AM    3    0.86    0.00   25.54   25.75    0.00    0.64    0.00    0.00    0.00   47.21
    
    Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    Average:     all    0.66    0.00   21.50   39.33    0.00    2.03    0.00    0.00    0.00   36.48
    Average:       0    0.62    0.00   28.42   65.98    0.00    0.21    0.00    0.00    0.00    4.77
    Average:       1    0.48    0.00    5.54    3.13    0.00    4.82    0.00    0.00    0.00   86.02
    Average:       2    0.65    0.00   24.57   57.83    0.00    2.83    0.00    0.00    0.00   14.13
    Average:       3    0.86    0.00   25.54   25.75    0.00    0.64    0.00    0.00    0.00   47.21
    
  • 这里可以看到,1分钟的平均负载会增加到1.08,其中一个CPU的%sys使用率达到28.42%,同时iowait达到了65.95%,这说明负载的升高是由于iowait升高引起的;

  • 查找哪个进程导致iowait升高,还是使用pidstat命令,pidstat -d命令可以查看具体进程的IO读写情况:

    # 间隔5秒后输出一组数据,-u表示CPU指标
    $ pidstat -u 5 1
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    04:40:24 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    04:40:29 AM    27      2269    0.20    0.00    0.00    0.00    0.20     3  mysqld
    04:40:29 AM     0      6592    0.00   32.80    0.00    0.20   32.80     1  kworker/u256:1
    04:40:29 AM     0      7170    0.60    0.40    0.00    0.00    0.99     1  watch
    04:40:29 AM     0      7706    0.00   10.14    0.00    0.20   10.14     0  stress
    04:40:29 AM     0      7707    0.40   72.76    0.00    0.20   73.16     0  stress
    04:40:29 AM     0      7913    0.20    0.40    0.00    0.00    0.60     3  pidstat
    
    [root@localhost ~]# pidstat -d
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    04:58:53 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
    04:58:53 AM     0         1     24.89  22044.05   7471.21       9  systemd
    04:58:53 AM     0      1211      0.13      0.00      0.00       2  sshd
    04:58:53 AM     0      1222      0.11      0.16      0.11       2  rsyslogd
    04:58:53 AM     0      1230      5.06      0.00      0.00      44  containerd
    04:58:53 AM     0      1246      2.56      0.00      0.00      41  libvirtd
    04:58:53 AM     0      1250     10.64      0.05      0.00      58  dockerd
    04:58:53 AM     0      1262      0.03      0.01      0.00       2  crond
    04:58:53 AM     0      1271      0.01      0.00      0.00       0  agetty
    04:58:53 AM     0      1572      0.04      0.00      0.00       0  master
    04:58:53 AM    89      1577      0.04      0.00      0.00       1  qmgr
    04:58:53 AM     0      2055     23.92      9.71      0.01       0  java
    04:58:53 AM    27      2269      0.18      1.62      0.00       5  mysqld
    04:58:53 AM     0      2341      0.19      0.00      0.00       1  sshd
    04:58:53 AM     0      8497      0.00      0.00      0.00       6  kworker/2:1
    04:58:53 AM     0      8536      0.00      0.00    339.51    1872  stress
    04:58:53 AM     0      8537      0.00   2440.09    443.23     716  stress
    

    可以看到还是stress进程引起的。

案例三:大量进程的场景

  • 当系统中运行进程超出 CPU 运行能力时,就会出现等待 CPU 的进程,使用stress模拟16个进程的负载:

    $ stress -c 8 --timeout 600
    stress: info: [8303] dispatching hogs: 16 cpu, 0 io, 0 vm, 0 hdd
    
  • 由于系统中只有4个CPU,明显比16个进程要少,所以CPU处于严重过载的状态:

    [root@localhost ~]# uptime
     04:47:29 up  2:01,  4 users,  load average: 12.30, 4.62, 2.20
    
  • 然后再使用pidstat查看进程的情况:

    $ pidstat -u 5 1
    Linux 3.10.0-1127.el7.x86_64 (localhost.localdomain)    09/10/2020      _x86_64_        (4 CPU)
    
    04:48:11 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    04:48:16 AM     0         9    0.00    0.20    0.00    0.39    0.20     2  rcu_sched
    04:48:16 AM     0       960    0.00    0.20    0.00    0.79    0.20     2  vmtoolsd
    04:48:16 AM     0      1250    0.39    0.00    0.00    0.00    0.39     1  dockerd
    04:48:16 AM     0      2055    0.20    0.00    0.00    0.00    0.20     0  java
    04:48:16 AM     0      8304   25.15    0.00    0.00   74.26   25.15     1  stress
    04:48:16 AM     0      8305   24.95    0.00    0.00   74.85   24.95     0  stress
    04:48:16 AM     0      8306   25.34    0.00    0.00   74.26   25.34     1  stress
    04:48:16 AM     0      8307   25.93    0.00    0.00   73.67   25.93     0  stress
    04:48:16 AM     0      8308   24.36    0.00    0.00   75.05   24.36     3  stress
    04:48:16 AM     0      8309   25.54    0.20    0.00   73.87   25.74     0  stress
    04:48:16 AM     0      8310   26.13    0.00    0.00   72.69   26.13     2  stress
    04:48:16 AM     0      8311   23.97    0.00    0.00   75.44   23.97     3  stress
    04:48:16 AM     0      8312   24.56    0.00    0.00   75.44   24.56     1  stress
    04:48:16 AM     0      8313   24.56    0.00    0.00   74.85   24.56     2  stress
    04:48:16 AM     0      8314   23.97    0.00    0.00   75.25   23.97     1  stress
    04:48:16 AM     0      8315   24.17    0.00    0.00   75.25   24.17     0  stress
    04:48:16 AM     0      8316   23.97    0.00    0.00   76.42   23.97     3  stress
    04:48:16 AM     0      8317   25.74    0.00    0.00   73.87   25.74     2  stress
    04:48:16 AM     0      8318   23.97    0.00    0.00   76.23   23.97     2  stress
    04:48:16 AM     0      8319   24.36    0.00    0.00   74.66   24.36     3  stress
    04:48:16 AM     0      8431    0.20    0.20    0.00    0.79    0.39     3  pidstat
    
    

    可以看到,16个stress进程在争抢4个CPU,每个进程等待CPU的时间(即%wait列)高达75%,这些超出CPU计算能力的进程,最终导致了CPU的过载。

总结

通过这三个案例,再来归纳一下平均负载的理解。

平均负载提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但只看平均负载本身,我们并不能直接发现,到底是哪里出现了瓶颈。所以,在理解平均负载时,也要注意:

  • 平均负载高有可能是 CPU 密集型进程导致的;
  • 平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了;
  • 当发现负载高的时候,可以使用 mpstat、pidstat 等工具,辅助分析负载的来源。

# 性能优化