某个应用的CPU使用率达到100%,如何排查

CPU使用率是单位时间内CPU的使用情况的统计,以百分比的方式展现。那么,作为最常见也是最熟悉的CPU指标,CPU的使用率是如何算出来的?再有诸如top、ps之类的性能工具展示的%user、%nice、%system、%iowait、%steal 等等、如何区分他们之间不同呢?

今年我们将以nginx为例带你提供一步步操作和分析中深入了解CPU使用率的相关内容

CPU使用率

linux作为一个多任务操作系统,将每个CPU的时间划分为很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成多个任务同时运行的错觉。

为了维护CPU时间,linux通过事先定义的节拍率(内核中表示为HZ),触发时间中断,并使用全局变量Jiffies记录了开机以来的节拍数。每发生一次时间中断,Jiffies的值就加1.

节拍率HZ是内核的可配置选项,可以设置为100、250、1000等。 不同的操作系统可能设置不同数值,你可以通过查询/boot/config内核选项来查看它的配置值。比如在我的系统中,节拍率设置成了1000 ,也就是每秒钟触发1000次时间中断。

[root@localhost ~]# grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=1000

同时,正因为节拍率HZ是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供看一个用户空间节拍率USER_HZ, 它总是固定为100,也就是1/100秒。 这样用户空间并不需要关心内核中的HZ被设置成了多少,因为它看到的总是固定值USER_HZ.

linux通过/proc 虚拟文件系统,向用户空间提供了系统内部状态的信息,而/proc/stat提供的就是系统的CPU和系统统计信息。比方说,如果你只关注CPU的话,可以执行下面的命令:

#只保留各个CPU的数据
[root@localhost ~]# cat /proc/stat | grep ^cpu 
cpu  491 3 1893 2331674 5156 0 33 0 0 0
cpu0 327 0 954 1165578 2286 0 18 0 0 0
cpu1 164 2 939 1166096 2870 0 14 0 0 0

这里输出的是一个表格,其中,第一列表示的是cpu编号,如cpu0、cpu1, 而第一行没有编号的CPU,代表的是所有CPU的累加。其他列则表示不同场景下的CPU的累加节拍数,他的单位是USER_HZ,也就是10ms(1/100秒),所以这其实就是不同场景下的CPU时间。

当然,这里每一列的顺序并不需要补背下来。只要记住,在有需要的时间,查询man proc就可以。不过,你要清楚man proc .下面我就来依次解读。

  • ​ user(通常缩写为us),代表用户态CPU时间。注意,他不包括下面的nice时间,但是包括了guest时 间。
  • nice(通常缩写为ni),代表低优先级用户态CPU时间,也就是进程的nice值被调整为1-19之间时的CPU时间,这里注意nice的可取值范围是-20到19,数值越大,优先级反而越低。
  • system(通常缩写为sys),代表内核态CPU时间。
  • idle(通常缩写为id ),代表空闲时间。注意,它不包括等待I/O的时间(iowait)
  • iowait(通常缩写为wa),代表等待I/O的CPU时间。
  • irq(通常缩写为hi),代表处理硬中断的CPU时间
  • softirq(通常缩写为si),代表处理软中断的CPU时间。
  • steal(通常缩写为st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的CPU时间。
  • guest(通常缩写为guest),代表听过虚拟化运行其他操作系统的时间,也就是运行虚拟机的CPU时间。
  • guest_nice(通常缩写为gnice),代表以低优先级运行虚拟机的时间。

而未能通常所说的CPU使用率就是除了空闲时间外的其他时间占总CPU时间的百分比,用公式表示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAesBrZJ-1597392361457)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1595767601934.png)]

根据这个公式,我们就可以从 /proc/stat 中的数据,很容易地计算出 CPU 使用率。当然,
也可以用每一个场景的 CPU 时间,除以总的 CPU 时间,计算出每个场景的 CPU 使用率。

看到这里你应该想起来了,这是开机以来的节拍说累加值,所以直接算出来的,是开机以来的平均CPU使用率,一般没啥参考价值。

事实上,为了计算CPU使用率。性能工具一般都会取间隔一段时间(比如3秒)的两次值,作差后,再计算出这段时间内的平均CPU使用率,即

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKYDY7VI-1597392361458)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1595768084135.png)]

这个公式,就是我们用各种性能工具所看到的 CPU 使用率的实际计算方法。

现在,我们知道了 系统CPU使用率的计算方法,那进程的呢?跟系统的指标类似,linux也给每个进程提供了运行情况的统计信息,也就是/proc/[pid]/stat.不过,这个文件包含的数据就比较丰富,总共有52列的数据。

回过头来看,是不是说要查看 CPU 使用率,就必须先读取 /proc/stat 和 /proc/[pid]/stat
这两个文件,然后再按照上面的公式计算出来呢

当然不是,各种各样的性能分析工具已经帮我们计算好了。不过要注意的是,性能分析工具
给出的都是间隔一段时间的平均 CPU 使用率,所以要注意间隔时间的设置,特别是用多个
工具对比分析时,你一定要保证它们用的是相同的间隔时间。

比如,对比一下 top 和 ps 这两个工具报告的 CPU 使用率,默认的结果很可能不一样,因
为 top 默认使用 3 秒时间间隔,而 ps 使用的却是进程的整个生命周期。

怎么查看CPU使用率

知道了 CPU 使用率的含义后,我们再来看看要怎么查看 CPU 使用率。说到查看 CPU 使用
率的工具,我猜你第一反应肯定是 top 和 ps。的确,top 和 ps 是最常用的性能分析工
具:

  • top显示 了系统总体的CPU和内存使用情况,以及各个进程的资源使用情况。
  • ps则只显示了每个进程的资源使用情况。

比如,top的输出格式为:

# 默认每 3 秒刷新一次
# top
top - 21:27:05 up  6:46,  3 users,  load average: 0.00, 0.01, 0.05
Tasks: 119 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7990132 total,  7476364 free,   241296 used,   272472 buff/cache
KiB Swap:  8257532 total,  8257532 free,        0 used.  7497484 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                              
   448 root      20   0       0      0      0 S   0.3  0.0   0:08.47 kworker/1:2                          
   817 root      20   0  305500   6544   5144 S   0.3  0.1   0:18.92 vmtoolsd                             
     1 root      20   0  128108   6696   4096 S   0.0  0.1   0:01.76 systemd                              
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.01 kthreadd                             
     4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H                         
     5 root      20   0       0      0      0 S   0.0  0.0   0:00.50 kworker/u256:0                       
     6 root      20   0       0      0      0 S   0.0  0.0   0:00.50 ksoftirqd/0                          
     7 root      rt   0       0      0      0 S   0.0  0.0   0:00.05 migration/0                          
     8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh                               
     9 root      20   0       0      0      0 S   0.0  0.0   0:01.67 rcu_sched                            
    10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 lru-add-drain                        
    11 root      rt   0       0      0      0 S   0.0  0.0   0:00.06 watchdog/0                           
    12 root      rt   0       0      0      0 S   0.0  0.0   0:00.10 watchdog/1                           
    13 root      rt   0       0      0      0 S   0.0  0.0   0:00.19 migration/1                          
    14 root      20   0       0      0      0 S   0.0  0.0   0:00.39 ksoftirqd/1                          
    16 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/1:0H                         
    18 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kdevtmpfs                            
    19 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 netns                                
    20 root      20   0       0      0      0 S   0.0  0.0   0:00.00 khungtaskd                           
    21 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 writeback                            
    22 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kintegrityd                          
    23 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                               
    24 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                               
    25 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset                               
    26 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kblockd                              
    27 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 md                                   
    28 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 edac-poller                          
    29 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 watchdogd                            
    35 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kswapd0 
    

这个输出结果中,第三行 %Cpu 就是系统的 CPU 使用率,具体每一列的含义上一节都讲
过,只是把 CPU 时间变换成了 CPU 使用率,我就不再重复讲了。不过需要注意,top 默
认显示的是所有 CPU 的平均值,这个时候你只需要按下数字 1 ,就可以切换到每个 CPU
的使用率了。

继续往下看,空白行之后是进程的实际信息,每个进程都有一个%CPU列,表示进程的CPU使用率。它是用户态和内核态CPU使用率的总和,包括进程用户使用的CPU、通过系统调用执行的内核空间CPU、以及就绪队列等待运行的CPU。在虚拟化环境中,它还包括了运行虚拟机占用的CPU。

所以,到这里我们可以发现,top并没有细分进程的用户态CPU和内存态CPU。那要怎样查看每个进程的详细情况呢?

比如,下面的piddstat命令,就间隔1秒展示了进程的5组CPU使用率,包括:

  • ​ 用户态CPU使用率(%usr);

  • ​ 内核态CPU使用率(%system);

  • ​ 运行虚拟机CPU使用率(%guest);

  • ​ 等待CPU使用率(%wait);

  • ​ 以及总的CPU使用(%CPU)

    最后的 Average 部分,还计算了 5 组数据的平均值。

# 每隔 1 秒输出一组数据,共输出 5 组
[root@localhost ~]# pidstat 1 5 
Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain) 	2020年07月26日 	_x86_64_	(2 CPU)

21时46分48秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command

21时46分49秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command
21时46分50秒     0      3274    0.00    1.00    0.00    1.00     0  pidstat

21时46分50秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command

21时46分51秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command

21时46分52秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command
21时46分53秒     0      3274    1.00    0.00    0.00    1.00     0  pidstat

平均时间:   UID       PID    %usr %system  %guest    %CPU   CPU  Command
平均时间:     0      3274    0.20    0.20    0.00    0.40     -  pidstat

CPU使用率过高怎么办?

通过top、ps、pidstat等工具,你能够轻松找到CPU使用率较高(比如100%)的进程。接下来,你可能想知道,占用CPU的到底是代码里的哪个函数呢?找到之后更高效、更针对性地进行优化。

我猜你想到的第一个工具应该是GDB(The GNU Project Debugge), 这个功能强大的程
序调试利器。的确,GDB 在调试程序错误方面很强大。但是,我又要来“挑刺”了。请你
记住,GDB 并不适合在性能分析的早期应用。

为什么呢?因为 GDB 调试程序的过程会中断程序运行,这在线上环境往往是不允许的。所
以,GDB 只适合用在性能分析的后期,当你找到了出问题的大致函数后,线下再借助它来

进一步调试函数内部的问题。

那么哪种工具适合在第一时间分析进程的 CPU 问题呢?我的推荐是 perf。perf 是 Linux
2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事
件和内核性能,还可以用来分析指定应用程序的性能问题。
使用 perf 分析 CPU 性能问题,我来说两种最常见、也是我最喜欢的用法。
第一种常见用法是 perf top,类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者
指令,因此可以用来查找热点函数,使用界面如下所示:

[root@localhost ~]# perf top

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k7aNcJBa-1597392361459)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1595773643077.png)]

输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事
件总数量(Event count)比如说这个例子,perf 一共采集了366个CPU时种事件,二总采集事件则为2958301.

采样数需要我们特别注意。如果采样数过少(比如只有十几个),那下面的排序和百
分比就没什么实际参考价值了。
再往下看是一个表格式样的数据,每一行包含四列,分别是:

  • 第一列 Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。
  • 第二列 Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),
    如内核、进程名、动态链接库名、内核模块名等。
  • 第三列 Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动
    态链接库,而 [k] 则表示内核空间。
  • 最后一列 Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表

以上面的输出为例,我们可以看出,占用CPU时种最多的是kernellinux内核,不过它的比例也只有18%左右,说明系统没有CPU性能问题。perf top 的使用应该比较清晰了吧。

着再来看第二种常见用法,也就是 perf record 和 perf report。 perf top 虽然实时展示
了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续的分析。而
perf record 则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示。

[root@localhost ~]# perf record  # 按 Ctrl+C 终止采样
[ perf record: Woken up 11 times to write data ]
[ perf record: Captured and wrote 2.869 MB perf.data (58689 samples) ]
[root@localhost ~]# ^C
[root@localhost ~]# perf report # 展示类似于 perf top 的报告

在实际使用中,我们还经常为 perf top 和 perf record 加上 -g 参数,开启调用关系的采
样,方便我们根据调用链来分析性能问题。

案例

下面我们就以 Nginx + PHP 的 Web 服务为例,来看看当你发现 CPU 使用率过高的问题
后,要怎么使用 top 等工具找出异常的进程,又要怎么利用 perf 找出引发性能问题的函
数。

你的准备

以下案例基于 centos7,同样适用于其他的 Linux 系统。我使用的案例环境如下所
示:
机器配置:2 CPU,8GB 内存
预先安装 docker、sysstat、perf、ab 等工具,如:yum -y install docker sysstat httpd-tools perf

我先简单介绍一下这次新使用的工具 ab。ab(apache bench)是一个常用的 HTTP 服务
性能测试工具,这里用来模拟 Ngnix 的客户端。由于 Nginx 和 PHP 的配置比较麻烦,我
把它们打包成了两个 Docker 镜像,这样只需要运行两个容器,就可以得到模拟环境。
注意,这个案例要用到两台虚拟机,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6oh0rvqy-1597392361460)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1595774699467.png)]

你可以看到,其中一台用作 Web 服务器,来模拟性能问题;另一台用作 Web 服务器的客
户端,来给 Web 服务增加压力请求。使用两台虚拟机是为了相互隔离,避免“交叉感
染”。
接下来,我们打开两个终端,分别 SSH 登录到两台机器上,并安装上面提到的工具。
还是同样的“配方”。下面的所有命令,都默认假设以 root 用户运行,如果你是普通用户
身份登陆系统,一定要先运行 sudo su root 命令切换到 root 用户。到这里,准备工作就
完成了。
不过,操作之前,我还想再说一点。这次案例中 PHP 应用的核心逻辑比较简单,大部分人
一眼就可以看出问题,但你要知道,实际生产环境中的源码就复杂多了。
所以,我希望你在按照步骤操作之前,先不要查看源码(避免先入为主),而是把它当成一
个黑盒来分析。这样,你可以更好地理解整个解决思路,怎么从系统的资源使用问题出发,
分析出瓶颈所在的应用、以及瓶颈在应用中的大概位置。

操作和分析

接下来,我们正式进入操作环节。

首先,在第一个终端执行下面的命令来运行 Nginx 和 PHP 应用:

# docker run --name nginx -p 10000:80 -itd feisky/nginx
# docker run --name phpfpm -itd --network container:nginx feisky/php-fpm

然后,在第二个终端使用 curl 访问 http://[VM1 的 IP]:10000,确认 Nginx 已正常启动。
你应该可以看到 It works! 的响应。

#192.168.0.108是第一台虚拟机的ip地址
[root@localhost ~]# curl http://192.168.0.108:10000
It works!

接着,我们来测试一下这个 Nginx 服务的性能。在第二个终端运行下面的 ab 命令:

# 并发 10 个请求测试 Nginx 性能,总共测试 100 个请求
[root@localhost ~]# ab -c 10 -n 100 http://192.168.0.108:10000/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.108 (be patient).....done


Server Software:        nginx/1.15.4
Server Hostname:        192.168.0.108
Server Port:            10000

Document Path:          /
Document Length:        9 bytes

Concurrency Level:      10
Time taken for tests:   3.502 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      17200 bytes
HTML transferred:       900 bytes
Requests per second:    28.55 [#/sec] (mean)
Time per request:       350.220 [ms] (mean)
Time per request:       35.022 [ms] (mean, across all concurrent requests)
Transfer rate:          4.80 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:    70  331  89.5    349     482
Waiting:       69  331  89.5    349     482
Total:         71  331  89.4    350     482

Percentage of the requests served within a certain time (ms)
  50%    350
  66%    381
  75%    401
  80%    412
  90%    440
  95%    459
  98%    478
  99%    482
 100%    482 (longest request)

从ab的输出结果我们可以看到,nginx能承受的每秒平均请求数只有28.55. 你一定会吐槽,这个性能也太差劲了吧,那么问题出在哪呢?我们用top和pidstat来观察一下。

这次,我们在第二个终端,将测试的请求总数增加到 10000。这样当你在第一个终端使用
性能分析工具时, Nginx 的压力还是继续。
继续在第二个终端,运行 ab 命令:

[root@localhost ~]# ab -c 10 -n 100 http://192.168.0.108:10000/

接着,回到第一个终端运行 top 命令,并按下数字 1 ,切换到每个 CPU 的使用率:

[root@localhost nginx-high-cpu]# top
top - 23:03:09 up  8:22,  3 users,  load average: 3.05, 0.90, 0.42
Tasks: 133 total,   6 running, 127 sleeping,   0 stopped,   0 zombie
%Cpu0  : 99.3 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  : 99.3 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
KiB Mem :  7990132 total,  6500072 free,   402556 used,  1087504 buff/cache
KiB Swap:  8257532 total,  8257532 free,        0 used.  7283800 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                  
  3970 bin       20   0  336684   9360   1684 R  53.5  0.1   0:18.58 php-fpm                                  
  3972 bin       20   0  336684   9360   1684 R  39.2  0.1   0:18.84 php-fpm                                  
  3969 bin       20   0  336684   9364   1688 R  37.9  0.1   0:18.75 php-fpm                                  
  3971 bin       20   0  336684   9360   1684 R  33.2  0.1   0:18.69 php-fpm                                  
  3968 bin       20   0  336684   9364   1688 R  32.9  0.1   0:18.68 php-fpm                                  
   623 root      20   0   39080   5748   5428 S   1.3  0.1   0:04.10 systemd-journal                          
  3806 101       20   0   33092   2144    772 S   1.0  0.0   0:01.39 nginx                                    
  3616 root      20   0  836968  35452  15776 S   0.3  0.4   0:39.77 dockerd-current                          
  3763 root      20   0  341252   7056   1660 S   0.3  0.1   0:00.87 docker-containe                          
     1 root      20   0  128108   6760   4144 S   0.0  0.1   0:03.98 systemd                                  
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.31 kthreadd    

这里可以看到,系统中有几个 php-fpm 进程的 CPU 使用率加起来接近 200%;而每个
CPU 的用户使用率(us)也已经超过了 99%,接近饱和。这样,我们就可以确认,正是用
户空间的 php-fpm 进程,导致 CPU 使用率骤升。

那再往下走,怎么知道是 php-fpm 的哪个函数导致了 CPU 使用率升高呢?我们来用 perf
分析一下。在第一个终端运行下面的 perf 命令:

 -g 开启调用关系分析,-p 指定 php-fpm 的进程号 3971
perf top -g -p 3971

按方向键切换到 php-fpm,再按下回车键展开 php-fpm 的调用关系,你会发现,调用关
系最终到了 sqrt 和 add_function。看来,我们需要从这两个函数入手了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJz2Yfxe-1597392361461)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1595776248058.png)]

我们拷贝出 Nginx 应用的源码,看看是不是调用了这两个函数

# 从容器 phpfpm 中将 PHP 源码拷贝出来
[root@localhost nginx-high-cpu]# docker cp phpfpm:/app .
使用grep 查找函数调用
[root@localhost nginx-high-cpu]# grep sqrt -r app/ 
app/index.php:  $x += sqrt($x);
#grep add_function -r app/ # 没找到 add_function 调用,这其实是 PHP 内置函数

OK,原来只有 sqrt 函数在 app/index.php 文件中调用了。那最后一步,我们就该看看这
个文件的源码了:

[root@localhost nginx-high-cpu]# cat app/index.php 

哎呀呀,发现问题了吧,居然犯了如此幼稚的错误,测试代码没删除就直接发布了,现在我们把修复后的docker重新运行一下:

# 停止原来的应用
# docker rm -f nginx phpfpm
# 运行优化后的应用
# docker run --name nginx -p 10000:80 -itd feisky/nginx:cpu-fix
# docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:cpu-fix

接着,到第二个终端来验证一下修复后的效果。首先 Ctrl+C 停止之前的 ab 命令后,再运
行下面的命令:

[root@localhost ~]# ab -c 10 -n 10000 http://192.168.0.108:10000/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.0.108 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx/1.15.6
Server Hostname:        192.168.0.108
Server Port:            10000

Document Path:          /
Document Length:        9 bytes

Concurrency Level:      10
Time taken for tests:   5.591 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1720000 bytes
HTML transferred:       90000 bytes
Requests per second:    1788.59 [#/sec] (mean)
Time per request:       5.591 [ms] (mean)
Time per request:       0.559 [ms] (mean, across all concurrent requests)
Transfer rate:          300.43 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   4.3      0     308
Processing:     0    5  31.3      2     739
Waiting:        0    5  31.1      2     739
Total:          1    5  32.2      2     739

Percentage of the requests served within a certain time (ms)
  50%      2
  66%      3
  75%      3
  80%      3
  90%      4
  95%      5
  98%      7
  99%     31
 100%    739 (longest request)

从这里你可以发现,现在每秒的平均请求数,已经从原来的 27.9变成了1788.59。

你看,就是这么很傻的一个小问题,却会极大的影响性能,并且查找起来也并不容易吧。当
然,找到问题后,解决方法就简单多了,删除测试代码就可以了。

小结:

CPU 使用率是最直观和最常用的系统性能指标,更是我们在排查性能问题时,通常会关注
的第一个指标。所以我们更要熟悉它的含义,尤其要弄清楚用户(%user)、Nice(%nice)、系统(%system) 、等待 I/O(%iowait) 、中断(%irq)以及软中断
(%softirq)这几种不同 CPU 的使用率。比如说:

  • 用户 CPU 和 Nice CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进
    程的性能问题。
  • 系统 CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用
    的性能问题。
  • I/O 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现
    了 I/O 问题。
  • 软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重
    排查内核中的中断服务程序。

碰到 CPU 使用率升高的问题,你可以借助 top、pidstat 等工具,确认引发 CPU 性能问题
的来源;再使用 perf 等工具,排查出引起性能问题的具体函数。

你可能感兴趣的:(Linux性能优化系列)