Linux 服务器卡顿排查实录:从 load average 到具体进程的五步定位法

举报
yd_290186593 发表于 2026/10/30 12:41:12 2026/10/30
【摘要】 从 load average 判断 CPU/IO 瓶颈,到 pidstat、iostat、线程级定位,一套只用系统自带工具的五步排查法。含真实案例。

线上机器突然变慢,第一反应往往是「重启试试」。但重启只是把问题推迟,根因还在。这篇记录一套我常用的五步排查法,从最粗粒度的 load average 一路收敛到具体进程的具体线程,全程只用系统自带工具,不需要装任何东西。

第一步:看 load average,判断是 CPU 还是 IO

uptime
# 12:30:01 up 42 days,  3:17,  1 user,  load average: 12.48, 9.73, 4.21

三个数字分别是 1、5、15 分钟的平均负载。关键不是绝对值大小,而是**和 CPU 核数对比**:

nproc
# 2

2 核机器 load 12.48,等于每个核排队 6 个任务,已经严重过载。再看 1 分钟值远高于 15 分钟值(12.48 vs 4.21),说明是**刚刚突然起来的**,不是长期趋势。

但 load 高不等于 CPU 忙。Linux 的 load average 把「可运行状态」和「不可中断睡眠状态(D 状态)」的进程都算进去,后者主要是等 IO。所以要区分:

top -bn1 | head -5
# %Cpu(s):  8.3 us,  2.1 sy,  0.0 ni, 12.4 id, 77.2 wa,  0.0 hi,  0.0 si

`wa`(iowait)77.2%,`id`(idle)还有 12.4% —— 这不是 CPU 算力不够,是**磁盘 IO 卡住了**。如果是 `us` 高、`wa` 低,才是真的 CPU 密集型。

这一步就能砍掉一半的排查方向。

第二步:定位是哪个进程在吃资源

ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10

两条命令分别按 CPU 和内存排序。但 iowait 高的时候,光看 `%CPU` 不够,因为等 IO 的进程 CPU 占用很低。要看 IO:

# 需要 sysstat,没有就先 apt install sysstat
pidstat -d 1 5

输出里 `kB_rd/s` 和 `kB_wr/s` 是每秒读写量,`iodelay` 是 IO 等待时钟周期。`iodelay` 高的进程就是罪魁祸首。

如果没有 pidstat,用这个替代:

for pid in $(ps -eo pid --no-headers); do
  io=$(cat /proc/$pid/io 2>/dev/null | awk '/^read_bytes/{r=$2}/^write_bytes/{w=$2}END{print r+w}')
  [ -n "$io" ] && echo "$io $pid $(cat /proc/$pid/comm 2>/dev/null)"
done | sort -rn | head -10

第三步:确认磁盘本身是否健康

找到可疑进程后,先排除「是不是盘坏了」:

iostat -x 1 3

重点看三列:

- `%util`:设备利用率,长期接近 100% 说明磁盘饱和

- `await`:平均 IO 等待时间(毫秒),机械盘超过 20ms、SSD 超过 5ms 就不正常

- `aqu-sz`:平均队列长度,大于 2 说明在排队

如果 `%util` 100% 但 `await` 不高,可能是小 IO 密集;如果 `await` 也很高,盘本身可能有问题,去看 `dmesg`:

dmesg -T | grep -iE "error|fail|reset|timeout" | tail -20

第四步:进到进程内部看线程

假设定位到是某个 Java 服务:

top -H -p <PID>

`-H` 显示线程级。找出 CPU 或 IO 最高的线程 TID,转成十六进制:

printf '%x\n' <TID>

然后用 `jstack` 抓栈,搜索这个十六进制值,就能看到具体是哪段代码:

jstack <PID> | grep -A 30 "nid=0x<hex>"

非 Java 程序用 `gdb` 或 `perf`:

perf top -p <PID>

第五步:确认是否被外部因素拖累

有时候问题不在自己进程,而是邻居:

# 检查是否有大量 TIME_WAIT / CLOSE_WAIT
ss -s

# 检查是否被限流(cgroup)
cat /sys/fs/cgroup/cpu/cpu.stat 2>/dev/null
cat /sys/fs/cgroup/memory/memory.stat 2>/dev/null | head -5

# 检查是否触发 OOM
dmesg -T | grep -i "out of memory"

云服务器还要注意**云盘 IOPS 上限**。比如某家云的基础型云盘限制 2000 IOPS,跑数据库很容易打满,此时 `%util` 会到 100% 但盘本身没坏 —— 是配额限制。这种情况只能升配或换盘型。

一个真实案例

上周一台 2 核 2G 的机器 load 冲到 15,`wa` 80%。按上面五步走:

1. load 15 / 2 核 = 7.5 倍过载,`wa` 80% → IO 瓶颈

2. `pidstat -d` 显示是 `mysqld` 在狂写

3. `iostat` 显示 `%util` 100%,`await` 45ms → 磁盘饱和

4. `top -H -p <mysql_pid>` 找到写线程,`jstack` 等价的 `SHOW PROCESSLIST` 看到一条没有索引的全表更新

5. 加索引后 `wa` 降到 3%

根因是一条慢 SQL,不是机器不行。

小结

五步的顺序是从粗到细,每一步都在缩小范围:

| 步骤 | 工具 | 回答的问题 |

|---|---|---|

| 1 | `uptime` + `top` | CPU 忙还是 IO 忙 |

| 2 | `ps` / `pidstat` | 哪个进程 |

| 3 | `iostat` / `dmesg` | 盘是否健康、是否饱和 |

| 4 | `top -H` + `jstack`/`perf` | 进程内哪段代码 |

| 5 | `ss` / cgroup / dmesg | 是否外部限制 |

全程不用装监控系统,系统自带工具就够定位。真正难的不是命令,是**忍住不重启**。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。