首页 服务器应用

当CPU使用率达到100%该怎么办?

2021-11-12 17:36 知乎@咸鱼

不知道大家有没有遇到过服务器的CPU使用率达到了100%的情况,在实际生产环境中如果遇到了这种情况我们该怎么办?

接下来我就跟大家探讨一下:当CPU的使用率达到了100%的时候我们该如何排查、定位、找出问题根源。

CPU使用率

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

CPU使用率:就是CPU处在非空闲状态的时间占总CPU时间的百分比

而CPU使用率中也有很多相关的重要指标,我们可以通过 top 命令简单列举出来

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 时间。

如何查看CPU使用率

在介绍查看CPU使用率的工具之前,我们先来想一个问题:这些性能工具是怎么计算CPU使用率的?

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

这个公式,就是大多数性能工具所看到的CPU使用率的实际计算方法。所以,在使用这些工具的时候我们要注意间隔时间的设置。

top 工具和 ps 工具

top 和 ps 是最常用的性能分析工具,其中:

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

ps 显示每个进程的资源使用情况。

sysstat 工具

除了上面这两个最常用的工具之外,我们还可以使用 sysstat 工具中的 pidstat 命令来查看进程的CPU使用率。

格式:pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]

# 常用选项

-u:默认的参数,显示各个进程的cpu使用统计。

-r:显示各个进程的内存使用统计。

-d:显示各个进程的IO使用情况。

-p:指定进程号。

-w:显示每个进程的上下文切换情况。

-t:显示选择任务的线程的统计信息外的额外信息。

上面这些工具你可以轻松找出哪个CPU使用率较高的进程,但是找到进程还不够,我们还想找出是哪个具体进程或者具体函数占用了如此高的CPU时间,这样才能进行更好的优化、

这里我推荐一个可以在第一时间分析进程的CPU问题的工具 ——perf

perf top

perf 是一种 Linux 内置的性能分析工具。它以性能事件采样为基础不仅可以分析系统的各种事件和内核进程,还可以用来分析指定应用程序的性能问题。

perf top 类似于 top,它能够实时显示占用CPU时钟最多的函数或者指令,因此可以查找出热点函数

$ perf top

Samples: 833 of event 'cpu-clock', Event count (approx.): 97742399

Overhead    Shared  Object   Symbol

7.28%        perf                   [.] 0x00000000001f78a4

4.72%       [kernel]              [k] vsnprintf

4.32%       [kernel]              [k] module_get_kallsym

3.65%       [kernel]              [k] _raw_spin_unlock_irqrestore

从输出结果我们可以看出:

第一行包含三个数据:采样数(Samples)、事件类型(event)和事件总数量(Event count)。这个例子中 perf 总共采集了833个CPU时钟时间,而总事件数为97742399

接着我们从列的角度来看:

第一列:Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。

第二列::Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。

第三列 :Object 是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核空间。

第四列:Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

perf record 、perf report

perf top 虽然实时展示了系统的性能信息,但是它并不能保存数据,就意味着无法用于离线或者后续的分析。

而perf record则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示。

$ perf record # 按Ctrl+C终止采样

[ perf record: Woken up 1 times to write data ]

[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]

$ perf report # 展示类似于perf top的报告。有时候还会加上-g参数来开启调用关系的采样案例。

下面我将用极客时间里的一个例子,来展现当我们发现CPU使用率过高的问题后,要怎么使用各种性能工具找出异常的进程,又要怎么利用各种工具找出引发性能问题的函数。

这次案例里面,我们预先安装了 sysstat、perf、ab 等工具

左边这台用作 Web 服务器,来模拟性能问题;右边用作客户端,来给 Web 服务器增加压力请求。

首先运行我们的 web 服务器,运行之后验证一下 Nginx 是否正常开启。

$curl 192.168.1.1:80

It works!

接下来我们在客户端测试一下 web 服务器的性能。

# 并发10个请求测试Nginx性能,总共测试100个请求

$ ab -c 10 -n 100 http://192.168.1.1:80

This is ApacheBench, Version 2.3 <$Revision: 1706008 $>

Copyright 1996 Adam Twiss, Zeus Technology Ltd,

...Requests per second: 11.63 [#/sec] (mean)

Time per request: 859.942 [ms] (mean)...

从ab的输出可以看到,Nginx能承受的每秒平均请求数只有11.63。这个结果说明Nginx 目前的性能不尽人意,我们先用top和pidstat看一下到底是哪里出了问题。

运行ab命令,持续给Nginx压力,方便我们使用性能分析工具。

#并发10个请求,总共10000个请求。

$ ab -c 10 -n 10000 http://192.168.1.1:80

运行 top 命令,按下数字1,切换到每个CPU使用情况(单核系统不需要按)

$ top

...

%Cpu0 : 98.7 us, 1.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

%Cpu1 : 99.3 us, 0.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

...

PID  USER PR  NI  VIRT  RES  SHR  S  %CPU  %MEM  TIME+  COMMAND

21514 daemon 20 0 336696 16384 8712 R 41.9 0.2 0:06.00 php-fpm

21513 daemon 20 0 336696 13244 5572 R 40.2 0.2 0:06.08 php-fpm

21515 daemon 20 0 336696 16384 8712 R 40.2 0.2 0:05.67 php-fpm

21512 daemon 20 0 336696 13244 5572 R 39.9 0.2 0:05.87 php-fpm

21516 daemon 20 0 336696 16384 8712 R 35.9 0.2 0:05.61 php-fpm

这里我们可以看到:

系统中这几个php-fpm进程的CPU使用率加起来将近200%;而每个CPU的用户态使用率(us)也已经超过了98%

所以我们可以得出结论:用户空间的php-fpm进程,导致了CPU使用率骤升。

接下来我们需要找出 php-fpm 进程里面哪个函数导致了CPU使用率的升高。

首先运行我们的 perf top 命令,实时分析进程的CPU问题。

# -g参数开启调用关系分析,-p指定php-fpm的进程号21515

$ perf top -g -p 21515

按方向键切换到php-fpm,再按下回车键开php-fpm的调用关系,你会发现,调用关系最终到了sqrt 和add_ function函数。

找出了函数后我们再查看源码并修改优化

优化后我们再测试一下

$ ab -c 10 -n 10000 http://192.168.1.1:80

...

Complete requests: 10000

Failed requests: 0

Total transferred: 1720000 bytes

HTML transferred: 90000 bytes

Requests per second: 2237.04 [#/sec] (mean)

Time per request: 4.470 [ms] (mean)

Time per request: 0.447 [ms] (mean, across all concurrent requests)

Transfer rate: 375.75 [Kbytes/sec] received

返回首页
返回顶部