服务器 CPU 负载过高排查:top 与 pidstat 定位进程级问题

网站突然变慢、SSH 登录卡顿、面板告警说负载飙升——遇到服务器 CPU 负载过高,很多人的第一反应是重启,但重启只能暂时掩盖问题。本文教你一套不打断业务的排查路径:先用 top 判断负载水位和瓶颈方向,再用 pidstat 深挖到进程与线程级别,最后把结论落到可执行的优化动作上,帮助你把”负载高”三个字变成具体到 PID 的整改清单。

先看懂负载:load average 不等于 CPU 使用率

登录服务器后执行 uptime 或 top,会看到三个数字,比如 load average: 8.42, 6.10, 3.75,分别代表最近 1 分钟、5 分钟和 15 分钟的平均负载。它统计的是”正在运行 + 等待运行 + 等待磁盘 I/O 的任务数”,而 CPU 使用率只反映 CPU 时间的占用比例,两者含义不同。

判断负载是否真的过高,要除以核心数。假设这是一台 4 核的 VPS(虚拟专用服务器),负载 4 左右说明刚好打满;负载 8 说明有一半任务在排队;如果 15 分钟均值持续高于核心数,而 1 分钟均值还在上升,说明问题正在恶化。反过来,1 分钟冲高但 15 分钟均值平稳,通常是某个瞬时任务(比如备份、压缩)引起的,先观察再动手。

还有一个容易忽略的信号:负载高但 CPU 使用率不高。这多半意味着任务卡在磁盘 I/O 上,也就是常说的 iowait 偏高。这时看 CPU 使用率排最强进程往往找不到元凶,需要换工具从另一个角度看。关于基础指标怎么采集、什么时候该上监控,可以参考这篇 Linux 服务器监控排障:用 Prometheus 定位 CPU 与磁盘问题,把单次排查变成持续的指标观测。

负载水位与容量对比:超出容量线的橙色柱代表排队任务

服务器 CPU 负载过高排查封面图:放大镜聚焦机柜中一台高负载服务器单元

第一步:用 top 判断瓶颈方向

top 是排查的第一站,重点不是盯着 %CPU 排行,而是看屏幕顶部的几行汇总:

  • us(用户态):应用代码本身在消耗 CPU,比如 PHP 脚本死循环、图片处理任务堆积
  • sy(内核态):系统调用频繁,常见于进程频繁创建销毁、上下文切换过多
  • wa(iowait):CPU 在等磁盘,存储是瓶颈而不是算力
  • load average 与 Tasks 行:看运行队列长度和进程总数是否异常

按 1 可以展开每个核心的占用,判断负载是集中在单核(单线程瓶颈)还是均匀分布在所有核心(并发压力)。按 P 按CPU 排序、按 M 按内存排序,先记下最前面的几个 PID。如果 %CPU 列总和远低于负载值,大概率是 iowait 或不可中断进程(状态列为 D)在拖高负载。

这里有一个真实案例:某 WordPress 站点负载持续 12,top 里 us 只有 30%,wa 却到了 45%。最后定位到是数据库表损坏导致查询疯狂读盘,换到更快的存储并修复表之后负载立刻回落。这类”负载高但 CPU 不忙”的场景,下一步就该用 pidstat 从进程维度继续拆。

第二步:用 pidstat 定位到具体进程

sysstat 包里的 pidstat 是 top 之后的自然延伸:top 告诉你”系统整体在忙”,pidstat 告诉你”是哪个进程、忙在哪种资源上”。多数发行版通过 yum install sysstat 或 apt install sysstat 安装。三个最常用的模式:

# 每 2 秒采样一次,输出 CPU 维度的进程排行,共采 5 次
pidstat -u 2 5

# 输出内存维度,重点看 RSS 常驻内存和 %MEM
pidstat -r 2 5

# 输出磁盘 I/O 维度,iowait 高时用它找读写最频繁的进程
pidstat -d 2 5

pidstat -u 的输出里,%usr 是用户态消耗、%system 是内核态消耗、%CPU 是合计。结合 top 的方向来读:如果 top 显示 us 高,就在 -u 输出里找 %usr 靠前的进程;如果 wa 高,就切到 -d 看 kB_rd/s、kB_wr/s 谁在疯狂读写。找到目标 PID 后,用 pidstat -t -p <PID> 2 5 下钻到线程级,多线程应用(如数据库、Java 服务)经常是某一个线程在拖累整个进程。

一个实用技巧是把输出落盘便于事后分析:pidstat -urd 2 3600 > /tmp/pidstat.log &。负载是波动的,你在 SSH 里盯十分钟未必能抓到峰值,让它在后台采样一小时,再用 grep 按时间点回溯,比现场蹲守高效得多。如果定位结果显示内存吃紧、swap 频繁,排查思路要切换到内存侧,可以继续阅读 Linux swap 交换分区调优:服务器内存压力管理实战。

从系统到进程的定位漏斗:众多进程收窄到单个高占用进程

第三步:区分三类高负载,对症处理

排查到进程级之后,处理方式取决于负载属于哪一类。凭经验,三类问题的处方完全不同,用错方向只会浪费时间:

用户态高负载(us 高):常见于业务代码问题。PHP 网站要检查死循环、正则回溯爆炸、图片缩放任务堆积;采集类脚本则检查爬取频率。用 pidstat -t -p <PID> 找到最忙的线程后,strace -p <TID> 能看到它卡在哪个系统调用上。临时缓解先限流或降级,根治要靠改代码。

内核态高负载(sy 高):通常是进程调度压力。典型场景是短命进程风暴——PHP-FPM(PHP 快速进程管理器)的 max_requests 设得太小,进程被频繁销毁重建;或者 cron 任务配置过密。用 vmstat 1 看 cs 列(上下文切换次数),每秒超过几十万说明调度压力过大,应调大进程复用参数而不是加机器。

I/O 等待高负载(wa 高):瓶颈在磁盘。先看 pidstat -d 里谁在读写,再确认是随机小 I/O 还是顺序大 I/O。数据库服务器尤其怕随机小 I/O,常见解法是加内存扩大缓存、把日志和数据放到不同盘、或直接换 NVMe 存储。如果读写量本身不大但 wa 仍高,要检查磁盘健康状态,坏道和 RAID 降级都会导致性能断崖。

IOWait 存储瓶颈示意:处理器等待慢速磁盘返回数据

线程级深挖与真实案例

有个案例很典型:一台跑着多个站点的服务器,负载长期在核心数的两倍左右,top 里 us 高达 70%,但所有 PHP-FPM 进程的 %CPU 都很平均,看不出谁是元凶。换用 pidstat -t -u -p all 2 5 输出线程级数据后,问题立刻现形——其中一个站点的某个插件在每次请求里都做全表扫描式的数组排序,它对应的工作线程 %CPU 常年 95% 以上。停用插件后负载从 16 降到 3。

这个案例说明线程级视角的价值:进程级平均数会掩盖单线程热点。排查数据库、消息队列、Java 应用这类多线程服务时,pidstat -t 应该是默认动作,而不是备选项。

如果排查的结论是”业务确实增长、资源确实不够”,那就该考虑扩容而不是继续优化代码。判断标准很简单:pidstat 显示 busiest 进程的 %CPU 长期贴近 100% 且代码没有明显优化空间、wa 不高、扩容后负载能线性回落,就说明是真实的算力缺口。这时可以评估升级现有 VPS 配置,或者参考这篇 Netdata 实时监控部署:服务器性能瓶颈秒级定位,先把瓶颈看清楚,再决定是优化代码还是把重负载业务迁到独立资源更多、I/O 更强的方案上。

排查节奏建议:从 5 分钟检查到持续观测

负载告警触发时,建议按固定的节奏走,避免东查一下西看一下:

  1. 前 1 分钟:uptime 看趋势,top 按 1 和 P 确认瓶颈方向,记录 us/sy/wa 三个数
  2. 第 2-3 分钟:按方向选 pidstat 模式(-u、-d 或 -r),锁定目标 PID,必要时 -t 下钻线程
  3. 第 4-5 分钟:strace 或查应用日志确认根因,决定是限流、改配置还是扩容
  4. 处理后 30 分钟:再看一次 15 分钟 load average 是否回落到核心数以内

对于经常半夜被告警叫醒的同学,事后复盘比现场救火更重要。把每次排查的时间线、pidstat 输出和处理动作记下来,几次之后你会发现同类应用的问题模式高度重复。如果希望告警本身更聪明、少一些无效打扰,可以参考这篇 服务器监控告警配置:Prometheus 与 Grafana 落地清单,把 CPU、负载、iowait 一起纳入分级告警。

总结与下一步建议

核心排查思路可以浓缩成一句话:top 定方向,pidstat 定进程,线程级下钻定根因,按 us/sy/wa 三类分别施治。三个容易踩的坑:只看 CPU 使用率不看负载,会错过 I/O 型问题;止步于进程级平均数,会漏掉单线程热点;发现资源不够就盲目重启,不如留存 pidstat 证据再决定优化还是扩容。

如果你正在管理多台服务器、希望有更省心的环境——我们建议的做法是选择默认带资源监控、支持一键快照回滚的主机方案。Hostease 的 VPS 和独立服务器提供 NVMe 存储与中文技术支持,遇到负载问题可以直接提交工单让工程师协助定位。建议先从一台业务最关键的机器开始建立 pidstat 采样习惯,把被动救火变成主动容量规划;可以考虑把本文的排查步骤整理成内部 runbook,团队任何人遇到告警都能按同一套路径处理。

发表评论