Linux 服务器卡顿排查实录:从 load average 到具体进程的五步定位法
线上机器突然变慢,第一反应往往是「重启试试」。但重启只是把问题推迟,根因还在。这篇记录一套我常用的五步排查法,从最粗粒度的 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 | 是否外部限制 |
全程不用装监控系统,系统自带工具就够定位。真正难的不是命令,是**忍住不重启**。
- 点赞
- 收藏
- 关注作者
评论(0)