服务器CPU高问题排查思路

原创:linux09/28/2020发布pv:0uv:0ip:0twitter #linux

原文地址:https://www.douyacun.com/article/aac2ceebe1b2d3d706620a193feb580e

CPU高的原因

负载首先看的是top(htop也可以),可以定位到是哪个进程(线上服务也不会运行多个服务的)

但是进程是因为什么导致的进程负载高?

  • CPU密集型进程: 使用大量CPU会导致平均负载高,与平均负载一致
  • I/O密集型: 等待I/O也会导致平均负载升高,但是CPU使用率不一定很高
  • 上下文切换: 大量的上下文切换也会导致CPU负载变高,此时CPU 使用率也会比较高
    • 进程上下文切换
      • 内核调度(优先级/时间片)
      • 系统资源不足
      • sleep
      • 硬件中断
    • 线程上下文切换
    • 中断上下文切换

负载

单核处理器

"load average"的值越低,比如等于0.2或0.3,就说明电脑的工作量越小,系统负荷比较轻。

系统负荷为1.0,意味着大桥的所有路段都有车,也就是说大桥已经"满"了。但是必须注意的是,直到此时大桥还是能顺畅通行的

系统负荷为1.7,意味着车辆太多了,大桥已经被占满了(100%),后面等着上桥的车辆为桥面车辆的70%。

CPU的系统负荷,基本上等同于上面的类比。大桥的通行能力,就是CPU的最大工作量;桥梁上的车辆,就是一个个等待CPU处理的进程(process)。

经验法则

当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。

当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。

当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。

多处理器

2个CPU表明系统负荷可以达到2.0,此时每个CPU都达到100%的工作量。推广开来,n个CPU的电脑,可接受的系统负荷最大为n.0

工具

其他工具用法: 高负载定位的常用工具以及用法分析

Stress: (模拟负载高的场景)

详细用法来自: sparkdev

  • -v: verbose 详情

  • -q: quiet 程序在运行的过程中不输出信息

  • -n: dry-run 演练,并不实际执行

  • -t: timeout 超时

  • -c: 多少进程计算sqrt(平方根)

  • -i: 多少进程调用sync(脏页同步到磁盘)

  • -m: 多少进程调用内存malloc/free(内存申请释放)

  • --vm-bytes B 每个进程总计申请多少字节内存

  • --vm-stride B 内存赋值时间隔多少字节

    不断的给部分内存赋值,让 COW(Copy On Write)发生。只要指定了内存相关的选项,这个操作就会执行,只是大小为默认的 4096。赋值内存的比例由参数决定:

for (i = 0; i < bytes; i += stride)
ptr[i] = 'Z';           /* Ensure that COW happens.  */

bytes 为消耗的总内存大小,stride 为间隔。 该参数会影响 CPU 状态 us 和 sy:

  • us: 用户空间占用CPU百分比
  • sy: 内核空间占用CPU百分比
$ stress --vm 2 --vm-bytes 500M --vm-stride 64
$ top
%Cpu(s): 21.3 us, 32.1 sy
$ stress --vm 2 --vm-bytes 500M --vm-stride 1M
$ top
%Cpu(s):  0.7 us, 36.3 sy
  • --vm-stride 值增大就意味着减少赋值,用户空间占用CPU的比率就小了
  • --vm-hang N: 每个消耗内存的进程在分配到内存后转入睡眠状态 N 秒,然后释放内存,一直重复执行这个过程, CPU空闲出来了,US就会降低了
  • --vm-keep: 一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新分配内存)
$ stress --vm 2 --vm-bytes 500M --vm-keep
%Cpu(s): 50.4 us,  0.3 sy

$ stress --vm 2 --vm-bytes 500M --vm-hang 5
%Cpu(s):  1.8 us,  7.8 sy
  • -d: (--hadd) 产生 N 个不断执行 write 和 unlink 函数的进程(创建文件,写入内容,删除文件)

    • --hadd-bytes B 指定文件大小
    $ stress -d 3 --hdd-bytes 10M
    $ pidstat -d 3
    11时34分30秒   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
    11时34分33秒  1000     20879      0.00      1.33      0.00  java
    11时34分33秒     0     23086      0.00 308237.87 309581.40  stress
    11时34分33秒     0     23087      0.00 346640.53 347003.32  stress
    11时34分33秒     0     23088      0.00 329619.93 326591.36  stress
    

场景一:CPU密集型

$ stress -c 1 time 600
$ watch -d uptime

Every 2.0s: uptime
23:39:14 up 10 days, 15:03,  3 users,  load average: 1.17, 0.82, 0.40

$ mpstat -P ALL 5
23时35分39秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
23时35分44秒  all   26.35    0.00    1.00    0.00    0.00    0.05    0.00    0.00    0.00   72.60
23时35分44秒    0    0.60    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00   98.40
23时35分44秒    1  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
23时35分44秒    2    3.41    0.00    1.41    0.00    0.00    0.00    0.00    0.00    0.00   95.18
23时35分44秒    3    1.21    0.00    1.61    0.00    0.00    0.00    0.00    0.00    0.00   97.18

CPU密集型是因为进程进行了大量的运算工作的导致的,通过mpstat可以轻松判断出来、

todo: 如何对代码定位问题

特点:

  • usr 高,进程一直处于用户态运行
  • sys
  • iowait

场景二:I/O 密集型

$ stress -i 5 --timeout 600
$ watch -d uptime
Every 2.0s: uptime                                                                                  Sat Aug  8 00:18:56 2020
 00:18:56 up 10 days, 15:43,  3 users,  load average: 0.95, 0.75, 0.43
$ mpstat -P ALL 3
13时42分04秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
13时42分07秒  all    2.04    0.00   43.20    1.02    0.00    0.09    0.00    0.00    0.00   53.66
13时42分07秒    0    4.10    0.00   42.66    0.00    0.00    0.00    0.00    0.00    0.00   53.24
13时42分07秒    1    0.68    0.00   43.69    0.34    0.00    0.00    0.00    0.00    0.00   55.29
13时42分07秒    2    2.03    0.00   42.23    0.34    0.00    0.00    0.00    0.00    0.00   55.41
13时42分07秒    3    1.70    0.00   44.22    3.06    0.00    0.00    0.00    0.00    0.00   51.02
$ top
top - 13:49:30 up 12 days,  5:13,  5 users,  load average: 2.07, 1.08, 1.44
Tasks: 169 total,   1 running, 168 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.9 us, 41.4 sy,  0.0 ni, 55.0 id,  0.5 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem :  7924044 total,  1278284 free,  5345032 used,  1300728 buff/cache
KiB Swap:  2097148 total,  2095868 free,     1280 used.  2109988 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 3092 root      20   0    7308     96      0 D  28.2  0.0   0:09.30 stress
 3094 root      20   0    7308     96      0 D  27.9  0.0   0:09.37 stress
 3091 root      20   0    7308     96      0 D  27.6  0.0   0:09.36 stress
 3093 root      20   0    7308     96      0 D  27.6  0.0   0:09.31 stress
 3095 root      20   0    7308     96      0 D  26.9  0.0   0:09.35 stress

I/O密集型是因为进行了大量的磁盘读写导致的负载高,其实磁盘和内存之间是有 页缓存 来缓冲磁盘和内存速度差异的,

  • 低频的读写文件是不会引起iowait偏高的,iowait高的原因是因为调用 sync / fsync 频繁的刷新脏页导致iowait过高,此时是处于内核态运行的所以 sys也会偏高
  • 高频的大量读写文件会直接触发 脏页刷新,iowait/sys会较高

而上面sys较高,iowait低的原因,只是因为高频的调用了sync,没有脏页(没有高频的写文件)

场景三:大量进程的场景

$ stress -c 8 
$ watch -d uptime
Every 2.0s: uptime                                                                                                                Sun Aug  9 13:19:04 2020

 13:19:05 up 12 days,  4:43,  5 users,  load average: 7.44, 3.23, 1.30
$ mpstat -P ALL 3
14时32分28秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
14时32分31秒  all   99.42    0.00    0.58    0.00    0.00    0.00    0.00    0.00    0.00    0.00
14时32分31秒    0   99.67    0.00    0.33    0.00    0.00    0.00    0.00    0.00    0.00    0.00
14时32分31秒    1   98.00    0.00    2.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
14时32分31秒    2   99.67    0.00    0.33    0.00    0.00    0.00    0.00    0.00    0.00    0.00
14时32分31秒    3  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
$ pidstat -u 3
14时32分56秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command
14时32分59秒     0     19290    0.00    0.33    0.00    0.33     3  mpstat
14时32分59秒     0     19450    0.33    1.33    0.00    1.67     0  pidstat
14时32分59秒     0     19491   48.67    0.00    0.00   48.67     3  stress
14时32分59秒     0     19492   49.33    0.00    0.00   49.33     1  stress
14时32分59秒     0     19493   48.67    0.00    0.00   48.67     3  stress
14时32分59秒     0     19494   48.67    0.00    0.00   48.67     0  stress
14时32分59秒     0     19495   50.33    0.00    0.00   50.33     2  stress
14时32分59秒     0     19496   48.67    0.00    0.00   48.67     0  stress
14时32分59秒     0     19497   49.33    0.00    0.00   49.33     2  stress
14时32分59秒     0     19498   49.33    0.33    0.00   49.67     1  stress
$ vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 8  0   1280 1271004    148 1301392    0    0     0     1    5    1  2  1 97  0  0
 8  0   1280 1271000    148 1301380    0    0     0     3 4414 1760 98  2  0  0  0
 8  0   1280 1267432    148 1301368    0    0     0     0 4370 1636 98  2  0  0  0

系统是4H8G

  • mpstat的4个CPU usr 一直是100%
  • pidstat的8个stress进程,各占用每个CPU的50%
  • vmstat的 r 正在运行的进程队列是 8

场景四:大量的上下文切换

使用sysbench模拟大量的线程

$ sysbench --threads=10 --time=10 threads run
top - 09:18:26 up 14 days, 42 min,  2 users,  load average: 2.58, 0.66, 1.30
Tasks: 161 total,   1 running, 160 sleeping,   0 stopped,   0 zombie
%Cpu(s): 16.2 us, 76.0 sy,  0.0 ni,  7.8 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem :  7924044 total,  1689024 free,  5740484 used,   494536 buff/cache
KiB Swap:  2097148 total,  1800444 free,   296704 used.  1734212 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 7640 root      20   0   95116   3972   2824 S 351.5  0.1   1:41.20 sysbench
  • 负载高
  • sy 内核态CPU高
  • sysbench 进程
$ vmstat -S M 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 6  0    289   1649      0    482    0    0     1     3    8    1  2  1 97  0  0
 7  0    289   1649      0    482    0    0     0     1 23407 347642 14 76  9  0  0
 7  0    289   1648      0    482    0    0     0     0 21909 339189 16 76  8  0  0
 7  0    289   1649      0    482    0    0     0     0 23330 344468 16 75  9  0  0
  • r: 运行队列高
  • in:中断高
  • cs:上下文切换次数高
$ pidstat -p 7640 -wt 3
09时21分22秒   UID      TGID       TID   cswch/s nvcswch/s  Command
09时21分25秒     0      7640         -      0.00      0.00  sysbench
09时21分25秒     0         -      7640      0.00      0.00  |__sysbench
09时21分25秒     0         -      7641   8870.00  23660.33  |__sysbench
09时21分25秒     0         -      7642  10428.00  20367.67  |__sysbench
09时21分25秒     0         -      7643  10766.67  19406.67  |__sysbench
09时21分25秒     0         -      7644  10391.67  18603.00  |__sysbench
09时21分25秒     0         -      7645  10480.00  21960.33  |__sysbench
09时21分25秒     0         -      7646  10012.67  18224.00  |__sysbench
09时21分25秒     0         -      7647   9655.00  18724.33  |__sysbench
09时21分25秒     0         -      7648  10216.33  20583.00  |__sysbench
09时21分25秒     0         -      7649  10317.67  20421.00  |__sysbench
09时21分25秒     0         -      7650  10450.67  20246.67  |__sysbench

通过top知道负载高的原因

  • 内核态的运行导致

vmstat定位负载高的原因:

  • 上下文切换高导致
  • 中断导致
  • 运行队列有等待运行进程

pidstat定位负载高的原因:

  • 可以看到sysbench 出来很多的子线程

负载与参数

分析

  • CPU使用率高,如何定位进程和是进程的哪个函数导致的?

CPU使用率高,如何定位进程和是进程的哪个函数导致的?

CPU使用率高定位是哪个进程,我们可以使用的工具有:

  • top/htop
  • pidstat
  • ps

如何定位是进程的哪个函数导致的?