服务器故障排查

1️⃣ 负载检查(uptime

  • 定义: CPU 负载(cpu load)指的是单位时间内进程对系统产生的压力(单位时间内运行队列中的平均进程数)

    • 如果一个进程满足以下条件则其就会位于运行队列中:
      • **可运行状态 R: **正在执行或者已经就绪的进程;
      • 不可中断睡眠状态 D 等待磁盘 IO、网络设备 IO 的进程;
    • 下列进程不满足条件:
      • 可中断睡眠 S 比如在 sleep(),或者阻塞等普通事件;
      • 僵尸进程 Z 当前进程已经结束,但是父进程没有等待子进程;
      • 暂停进程 T 当前进程被暂停了,比如接收到了 Ctrl + C 或者 GDB
  • 命令: uptime

    1
    2
    3
    $ uptime
    // 1m 5m 15m
    20:15:03 up 10 days, 3:42, 2 users, load average: 0.15, 0.20, 0.18
    • load average :在单位时间内,有多少个进程在占用或者等待系统资源;

2️⃣ 内核日志检查(dmesg

  • 定义: 查看 Linux 内核打印出来的消息(系统启动、磁盘信息、网卡信息、OOM 等)

  • 命令: dmesg

    1
    $ dmesg | tail -n 200
    • 查看最新内核日志消息

3️⃣ 实时查看系统当前资源占用情况(top

  • 定义: 常用来实时观察系统情况(CPU 利用率、Mem 利用率、系统负载、运行/睡眠/僵尸进程数量)

  • 命令:top

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ top
    20:41:12 up 10 days, 3:26, 2 users, load average: 0.18, 0.25, 0.31
    Tasks: 256 total, 1 running, 255 sleeping, 0 stopped, 0 zombie
    %Cpu(s): 3.2 us, 1.1 sy, 0.0 ni, 95.1 id, 0.3 wa, 0.0 hi, 0.3 si, 0.0 st
    MiB Mem : 7820.4 total, 1240.6 free, 2650.1 used, 3929.7 buff/cache
    MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 4620.5 avail Mem

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    3251 mysql 20 0 1623548 412320 18240 S 6.3 5.1 32:11.54 mysqld
    1820 root 20 0 512344 28420 10240 S 2.0 0.4 5:20.13 containerd
    2418 root 20 0 1122336 65328 15872 S 1.7 0.8 18:42.66 java
    936 root 20 0 189200 9424 7744 S 0.7 0.1 1:03.44 sshd
    1142 root 20 0 69240 5100 3200 S 0.3 0.1 0:10.26 systemd
    3310 www-data 20 0 245680 17480 6208 S 0.3 0.2 2:41.88 nginx
    1 root 20 0 167920 12028 8408 S 0.0 0.1 3:15.20 systemd
    • 常用用法:

      • top -H -p pid:查看进程 pid 中所有线程情况

        1
        2
        3
        4
        5
         PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
        2418 root 20 0 1122336 65328 15872 S 1.7 0.8 18:42.66 java
        2420 root 20 0 1122336 65328 15872 S 0.7 0.8 3:11.22 GC
        2421 root 20 0 1122336 65328 15872 S 0.5 0.8 2:52.11 VM
        2422 root 20 0 1122336 65328 15872 R 0.4 0.8 1:20.55 worker-1

4️⃣ 查看进程状态(pidstat

  • 定义: 经常用于检查 CPU 使用率异常上下文切换频繁 的问题;
  • 作用: 进一步定位是什么进程造成的,背后的具体原因是什么;
  • 常用命令:
    • -p <pid>:监控指定进程;
    • -t:显示进程信息;
    • -u:显示 CPU 使用情况;
    • -r:显示内存占用情况(kb虚拟内存/kb常驻内存);
    • -d:显示 I/O 使用情况(磁盘读写速率);
    • -w:显示上下文切换信息;

5️⃣ 查看某时刻的进程快照(ps

  • 定义:ps 用来查看 **某一时刻 **系统中的进程信息,本质上是一次性的静态快照。常用于查看进程是否存在、进程状态、父子进程关系、线程信息等。

  • 常用命令:

    • ps aux

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      $ ps aux
      USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
      root 1 0.0 0.1 167920 12028 ? Ss Mar22 3:15 /sbin/init
      root 936 0.1 0.1 189200 9424 ? Ss Mar22 1:03 /usr/sbin/sshd -D
      root 1820 2.0 0.4 512344 28420 ? Ssl Mar22 5:20 /usr/bin/containerd
      root 2418 1.7 0.8 1122336 65328 ? Sl Mar22 18:42 /usr/bin/java -jar app.jar
      mysql 3251 6.3 5.1 1623548 412320 ? Sl Mar22 32:11 /usr/sbin/mysqld
      www-data 3310 0.3 0.2 245680 17480 ? S Mar22 2:41 nginx: worker process
      root 4521 0.0 0.0 11544 4020 pts/0 Ss 20:48 0:00 -bash
      root 4630 0.0 0.0 12876 1120 pts/0 R+ 20:49 0:00 ps aux
    • ps -ef

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      $ ps -ef
      UID PID PPID C STIME TTY TIME CMD
      root 1 0 0 Mar22 ? 00:00:03 /sbin/init
      root 936 1 0 Mar22 ? 00:00:01 /usr/sbin/sshd -D
      root 1142 1 0 Mar22 ? 00:00:00 /lib/systemd/systemd --user
      root 1820 1 0 Mar22 ? 00:05:20 /usr/bin/containerd
      root 2418 1 1 Mar22 ? 00:18:42 /usr/bin/java -jar app.jar
      mysql 3251 1 6 Mar22 ? 00:32:11 /usr/sbin/mysqld
      www-data 3310 1 0 Mar22 ? 00:02:41 nginx: worker process
      root 4521 936 0 20:48 pts/0 00:00:00 -bash
      root 4603 4521 0 20:49 pts/0 00:00:00 ps -ef

6️⃣ 查看磁盘 IO 统计(iostat

  • 定义:查看系统的 CPU 使用情况和磁盘 IO 统计信息,常用于判断磁盘是否存在瓶颈。

  • 用法: iostat -xz 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ iostat -xz 1
    Linux 5.15.0-92-generic (test-host) 03/22/2026 _x86_64_ (4 CPU)

    avg-cpu: %user %nice %system %iowait %steal %idle
    3.21 0.00 1.12 0.45 0.00 95.22

    Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz d/s dkB/s aqu-sz %util
    sda 1.20 32.00 0.00 0.00 0.85 26.67 4.50 128.00 1.20 21.05 1.30 28.44 0.00 0.00 0.01 1.20
    nvme0n1 15.30 512.00 0.00 0.00 2.10 33.46 22.40 1024.00 3.60 13.85 3.25 45.71 0.00 0.00 0.09 8.60

7️⃣ 查看哪些进程在做 IO

  • 定义: iotop 用来实时查看哪些进程或线程正在产生磁盘 IO

  • 用法: iotop -oP

    • -o:只显示当前正在做 IO 的进程
    • -P:只显示进程,不展开线程
    1
    2
    3
    4
    5
    6
    7
    8
    $ sudo iotop -oP
    Total DISK READ : 1.20 M/s | Total DISK WRITE : 2.40 M/s
    Current DISK READ: 0.00 B/s | Current DISK WRITE: 1.00 M/s

    PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
    3251 be/4 mysql 0.00 B/s 820.00 K/s 0.00 % 12.45 % mysqld
    2418 be/4 root 0.00 B/s 120.00 K/s 0.00 % 2.31 % java -jar app.jar
    1820 be/4 root 64.00 K/s 32.00 K/s 0.00 % 0.85 % containerd

8️⃣ 查看网络的情况

  • 定义: ssLinux 下用于查看 socket 统计信息和网络连接状态的命令

  • 用法: ss -nltp

    1
    2
    3
    4
    5
    State   Recv-Q  Send-Q   Local Address:Port    Peer Address:Port  Process
    LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=936,fd=3))
    LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=3251,fd=21))
    LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3310,fd=6))
    LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=3310,fd=7))

9️⃣ 定位热点函数和分析调用栈(perf

  • 定义:perfLinux 提供的性能分析工具,常用来
    • 定位热点函数
    • 分析调用栈
    • 排查 CPU 高占用、锁竞争、上下文切换过多等性能问题。
  • 用法:
    • perf top:实时查看热点函数
    • perf record -F -g -p PID:对进程采样并记录调用栈
    • perf report -i :分析采样结果

项目案例

CPU 利用率太高如何去排查?

1️⃣ 背景

  为了说明 CPU 利用率过高时应该如何排查,直接拿 sylar 里的测试服务器做实验:

这个服务非常简单:

  • 监听 0.0.0.0:8020
  • 注册路由 /sylar/xx
  • 请求返回固定字符串 OK\n
  • 使用 sylar::IOManager iom(3),也就是 3 个调度线程

代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
sylar::http::HttpServer::ptr http_server(
new sylar::http::HttpServer(true, sylar::IOManager::GetThis(), sylar::IOManager::GetThis(),
false, nullptr, false, false));

sd->addServlet("/sylar/xx", [](sylar::http::HttpRequest::ptr req,
sylar::http::HttpResponse::ptr rsp,
sylar::http::HttpSession::ptr session) {
rsp->setHeader("Content-Type", "text/plain; charset=utf-8");
rsp->setBody("OK\n");
return 0;
});

sylar::IOManager iom(3);

可以看到,这里几乎没有业务逻辑,所以如果压测时 CPU 仍然很高,说明瓶颈更可能在:

  • 网络收发
  • HTTP 解析与响应拼装
  • 协程调度与线程切换
  • 系统调用频率过高

2️⃣ 复现

启动服务:

1
./root/workspace/sylar/bin/exe/my_http_server

CPU 采样: 同时用 pidstat 观察进程和线程的 CPU

1
pidstat -u -t -p <pid> 1 18

pidstat 结果

1
2
3
4
5
04:47:16 PM   UID      TGID       TID    %usr %system  %guest   %wait    %CPU   CPU  Command
04:47:17 PM 0 187221 - 11.00 12.00 0.00 0.00 23.00 1 my_http_server
04:47:17 PM 0 - 187221 3.00 5.00 0.00 0.00 8.00 1 |__my_http_server
04:47:17 PM 0 - 187222 6.00 2.00 0.00 0.00 8.00 2 |__Scheduler_0
04:47:17 PM 0 - 187223 2.00 6.00 0.00 0.00 8.00 0 |__Scheduler_1
  • %usrCPU 跑在 用户态 的比例,也就是应用自己的代码、C/C++ 运行库等;
  • %systemCPU 跑在 内核态 的比例,通常对应 系统调用内核 帮它做的事;
  • %waitCPU 等待 I/O 的时间百分比,例如 进程阻塞等待磁盘网络响应
  • %CPU:总体 CPU 占用率
  • 解析:
    • 死循环空轮询,通常会看到:
      • 没什么请求,CPU 还是高;
      • 某一个线程长期接近 100%
      • 吞吐量上不去;

重点

pidstat 里最关键的一点不是只有总 CPU,而是:

  • %usr
  • %system

这说明什么?

  • user CPU 高:用户态代码热点也不低
  • system CPU 高:系统调用和内核网络栈开销非常明显

也就是说,这个案例不能只盯着“是不是业务代码太重”,还要看:

  • send/recv
  • accept
  • epoll
  • 锁与线程唤醒

选择分析

  • syscall
    • 工具:strace

    • 命令:strace -c -p <pid> -p <tid1> -p <tid2>

    • 结果:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      % time     seconds  usecs/call     calls    errors syscall
      ------ ----------- ----------- --------- --------- ----------------
      70.96 6.635727 26 247345 sendto
      26.24 2.454384 9 250234 2817 recvfrom
      0.79 0.074287 14 4986 epoll_ctl
      0.76 0.070971 10 6745 rt_sigprocmask
      0.35 0.032592 11 2894 write
      0.23 0.021266 18 1149 86 futex
      0.20 0.018527 66 278 epoll_wait
    • 分析:

      • 大部分时间花在 sendto / recvfrom
      • epoll_ctlepoll_wait 也有开销
      • futex 说明线程同步不是完全没有成本
  • usercall
    • 工具:perf

    • 命令:

      • perf record -F -g --call-graph -p <pid> --sleep
      • perf report -i
    • 结果:

      • call-graph

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        std::function<void ()>::operator()
        -> sylar::http::HttpServer::handleClient
        -> sylar::http::HttpSession::sendResponse 36.17%
        -> sylar::Stream::writeFixSize 27.43%
        -> sylar::Socket::send 27.17%
        -> send / __x64_sys_sendto / tcp_sendmsg ...

        -> sylar::http::HttpSession::recvRequest 32.34%
        -> sylar::Socket::recv 15.59%
        -> recv / __x64_sys_recvfrom / tcp_recvmsg ...

        -> sylar::http::HttpResponse::dump 5.59%
      • self(平铺视角)

        1
        2
        3
        4
        5
        6
        7
        8
        9
        5.88%  __lock_text_start
        2.64% http_parser_execute
        1.62% syscall_enter_from_user_mode
        1.50% srso_alias_safe_ret
        1.49% std::__cxx11::basic_string<...>::_M_data
        1.10% malloc
        1.07% __swapcontext
        1.05% std::_Sp_counted_base<...>::_M_release
        1.00% _int_free

3️⃣ 排查路径

  • 通过 top / pidstat 确认是哪个进程和线程 CPU 利用率高;
  • 分清 user CPUsystem CPU
    • system 高就先看 strace -c
    • user 高就继续用 perf、火焰图、gdb 看热点函数
  • 回到代码定位到底是 空转锁竞争,还是 网络与协议处理成本 高;