Linux服务器故障排查实战:从系统负载异常到磁盘IO瓶颈定位

服务器故障排查的系统化方法

Linux服务器运行中出现性能下降或服务异常时,排查方向不明确是最浪费时间的环节。系统化排查的核心是先定位瓶颈层级——CPU、内存、磁盘IO、网络四者中谁先饱和,再深入对应子系统。整个过程不需要复杂工具,vmstat、iostat、pidstat三个命令覆盖80%的日常故障定位场景。

CPU负载异常的快速定位

当系统响应变慢,第一步用uptime查看负载均值:

$ uptime
 14:23:01 up 127 days,  3:15,  2 users,  load average: 8.52, 6.31, 3.28

负载均值8.52远超CPU核心数(假设4核),说明进程排队严重。但高负载不代表CPU是瓶颈——IO等待也会推高负载。需要用vmstat区分:

$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
12  2      0 234512  89340 567890    0    0   523    12  4231  8932 35  8 12 45  0
 8  3      0 198321  89120 568120    0    0   892     8  3987  9012 28  6  8 58  0

关键看wa列(IO Wait)和r列(运行队列)。wa值45%表示CPU大量时间在等待磁盘IO,r值12表示12个进程在等CPU时间片。此场景中wa是主因——磁盘IO阻塞导致CPU空闲等待,进而堆积进程队列。

如果wa接近0而r值高,说明是纯CPU瓶颈,用pidstat找出消耗CPU的进程:

$ pidstat -u 1 3
Linux 5.15.0-91-generic (server01)  08/03/2026  _x86_64_ (4 CPU)

02:14:01 PM   UID       PID    %usr    %system    %guest    %CPU   CPU  Command
02:14:02 PM  1000      2387    89.32      3.21      0.00   92.53     2  python3
02:14:02 PM  1000      3901     2.15      0.87      0.00    3.02     1  nginx
02:14:02 PM     0      1102     1.03      1.45      0.00    2.48     0  kubelet

PID 2387的python3进程占92.53% CPU。接下来用strace或perf进一步分析该进程的系统调用分布:

$ strace -c -p 2387
strace: Process 2387 attached
^Cstrace: Process 2387 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.23    0.234512          12     19542           write
 32.18    0.166789           8     20876           read
 12.43    0.064523          15      4301           futex
  9.16    0.047532          23      2071        12 openat

write和read调用占比77%,说明该进程在做大量IO操作。此时需确认它操作的文件路径:

$ ls -l /proc/2387/fd/ | head -20
cmd: python3  pid: 2387  fd: 0 -> /dev/pts/0
fd: 1 -> /dev/pts/0
fd: 3 -> /var/log/app/access.log
fd: 4 -> /data/cache/index.db

磁盘IO瓶颈深度定位

当vmstat显示wa值持续偏高,需要用iostat精确定位哪块磁盘、哪种IO类型是瓶颈:

$ iostat -xz 1 5
Device  r/s    w/s   rMB/s   wMB/s  avgrq-sz avgqu-sz  await r_await w_await %util
sda    128.3  45.2   12.5    2.3    256.0     8.92   51.2   35.6   78.4  89.3
sdb     12.1   8.3    0.5    0.4    128.0     0.12    5.8    4.2    8.1   2.1

sda的%util达89.3%,avgqu-sz为8.92(队列深度接近9),await 51.2ms远超正常值(SSD应小于5ms,HDD应小于20ms)。w_await 78.4ms更说明写操作严重阻塞。

定位到具体磁盘后,需要找出哪些进程在产生IO。iotop可以实时显示进程级IO使用:

$ iotop -oP
Total DISK READ: 12.5 M/s | Total DISK WRITE: 2.3 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN  IO    COMMAND
 2387  be/4 appuser   10.2 M/s    1.1 M/s    0.00 % 45.23 % python3 app.py
 3901  be/4 www-data   0.8 M/s    0.3 M/s    0.00 %  3.12 % nginx: worker
 1102  be/0 root       0.1 M/s    0.2 M/s    0.00 %  0.89 % kubelet

PID 2387的python3进程贡献了绝大部分读IO。结合strace的结论,该进程在密集读写/var/log/app/access.log和/data/cache/index.db。

针对这类日志密集写入场景,短期缓解方案包括:将日志输出改为异步缓冲写入、调整日志级别减少输出量、将日志文件挂载到tmpfs(如果不需要持久化)。长期方案则需要迁移到更高性能的存储介质或调整应用架构。

内存不足引发的问题诊断

内存问题通常表现为OOM Killer触发或swap使用量飙升。排查路径:

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          15Gi       12Gi       1.2Gi       256Mi       2.8Gi       1.8Gi
Swap:         8Gi        3.2Gi      4.8Gi

available只有1.8Gi,swap已用3.2Gi——内存严重不足,系统大量换页。查看哪个进程占用最多内存:

$ ps aux --sort=-%mem | head -10
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
appuser   2387  2.3 45.2 8567324 7234560 ?    Sl   Jun12 234:56 python3 app.py
mysql     4501  1.8 22.1 4231567 3523456 ?    Sl   Jun12 189:23 mysqld
redis     5102  0.5  8.3 1234567 1345672 ?    Ssl  Jun12  56:78 redis-server

python3进程RSS达7.2GB占45.2%内存,mysqld占3.5GB。需要判断python3的内存增长是否合理:

$ cat /proc/2387/status | grep -E 'VmRSS|VmSize|VmSwap'
VmSize:    8567324 kB
VmRSS:     7234560 kB
VmSwap:     987654 kB

VmSwap接近1GB,说明该进程已被OOM部分换出。检查dmesg确认是否有OOM Killer记录:

$ dmesg | grep -i "oom" | tail -5
[123456.789] Out of memory: Kill process 7890 (java) score 450 or sacrifice child
[123456.790] Killed process 7890 (java) total-vm:4123MB, anon-rss:856MB

确有进程被OOM Killer杀死。解决方案按优先级:优化python3应用的内存使用(检查是否有内存泄漏)、调整mysqld缓冲池大小、或增加物理内存。临时措施可以调整vm.swappiness降低换页倾向:

# 临时降低swap倾向
$ sysctl vm.swappiness=10
# 永久生效
echo "vm.swappiness=10" >> /etc/sysctl.conf

网络延迟的排查技巧

当CPU、内存、磁盘都不是瓶颈,但服务响应仍慢时,排查网络层。用ss查看连接状态分布:

$ ss -s
Total: 12543
TCP:   23456 (estab 8900, closed 12000, orphaned 456, synrecv 23, timewait 11000)

TIME_WAIT 11000个连接,占TCP总量近一半。高TIME_WAIT通常意味着短连接频繁创建销毁。检查具体哪个端口:

$ ss -tn state time-wait | awk '{print $4}' | cut -d: -f2 | sort | uniq -c | sort -rn | head
  4523 8080
  2100 3306
  1087 6379

8080端口TIME_WAIT最多,对应应用服务的出站连接。启用TCP连接复用可以缓解:

# 启用TIME_WAIT连接复用
$ sysctl net.ipv4.tcp_tw_reuse=1
# 减少TIME_WAIT超时时间
$ sysctl net.ipv4.tcp_fin_timeout=15

如果是延迟问题,用mtr持续追踪链路质量:

$ mtr --report --report-cycles 10 db.internal.example.com
HOST: server01                     Loss%   Snt   Last   Avg  Best  Wrst StDev
  1. gateway                      0.0%    10    0.3   0.4   0.2   0.8   0.2
  2. switch-core                  0.0%    10    0.5   0.6   0.4   1.2   0.3
  3. db.internal                   2.0%    10    1.2   1.5   1.0   3.2   0.8

第3跳有2%丢包和3.2ms最差延迟,可能存在网络拥塞或链路质量问题。结合sar -n DEV查看网卡的吞吐和错误计数,可以进一步确认是否到达带宽上限。

排查流程总结

完整排查路径遵循”四层递进”原则:先用uptime/vmstat确定瓶颈在CPU还是IO,再用pidstat/iostat定位到具体进程和磁盘,然后用strace/perf分析进程行为,最终用ss/mtr确认网络层是否有问题。每个层级都有对应的快速检测命令,关键是不跳步、不猜测,让数据驱动定位方向。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-fu-wu-qi-gu-zhang-pai-cha-shi-zhan-cong-xi-tong-fu/

(0)
小编小编
上一篇 19小时前
下一篇 19小时前

相关推荐