服务器入坑指北:负载高不等于 CPU 忙
负载很高,但 CPU 看着很闲,于是判断"机器没问题",直到服务开始超时;磁盘慢导致进程卡在不可中断状态,可常规视图里根本看不出来;每次抖动都说"再看看",始终没有一套可复用的分诊顺序。
第一件事是把"负载"这个词拆开:负载平均值里含两类——"可运行或正在运行"的进程,以及**"处于不可中断睡眠"的进程**(后者多半卡在 I/O 上)。所以"负载高 + CPU 闲"几乎总指向 I/O 或阻塞,而不是算力不足。
这篇给一条四步分诊顺序、三处压力指标(含两种口径),以及"占用不等于压力"这条纪律。文中不贴任何真实负载值、压力数值与核数。
先把"负载"拆开
机制上,负载平均值是按"处于运行或可运行状态的进程数"加上"处于不可中断睡眠状态的进程数"算出来的。
不可中断睡眠指的是:进程在内核里等 I/O,此时连强制信号都送不进去(这一点在《进程卡死与杀不掉的进程》里讲过)。这些进程同样被算进了负载——这就是"负载高而 CPU 却闲"的来源。
判据因此很直接:"负载高 + CPU 闲"几乎总指向 I/O 或阻塞,而不是算力不足。
顺序反了的代价:一上来就看 CPU 占用排行,结论会被引到"加 CPU"上,而真正的问题在别处。
还有一个配套指标要一起看:同一份工具还能给出"当前被阻塞、等待 I/O 完成的进程数"——它直接把"有多少进程在等盘"摆在你面前。
反过来说:负载高、CPU 也确实忙,才说明瓶颈有可能真的在算力上——这时第 3 步会落在 CPU 那一处的压力上,接下来才是"要不要设限、怎么设限"的问题。
再看"压力",而不是只看"用量"
内核会直接导出"因资源不足而停滞的时间占比",分 CPU / 内存 / I/O 三类,并且区分两种口径:
- “至少有一些进程在停滞”:有人被挡住,但机器还在干活;
- “所有非空闲进程同时停滞”:实际的 CPU 周期被浪费——长时间处于这个状态的工作负载被认为是 thrashing,所以内核把它单独统计、单独导出。
为什么"全都在等"更严重:"有人在等"是局部资源紧张——大部分进程还在推进,说明系统仍在有效工作;"全都在等"是整体被拖住——没有进程在有效推进,相当于这一段时间的算力被浪费掉了。这两者要分开看,不能只看其中一个。
另外两组读数也要用起来: - 几个时间窗的均值(越大越平滑)+ 累计停滞时间:累计值能抓到均值掩盖的时延毛刺——那种"每次只卡一下"的问题,均值上看不出来;
- 一个边界:系统级别上"CPU full"没有定义(只是为兼容而报为 0)。所以读 CPU 那一项时只看 some,别把 full 为 0 当成"没压力"的证据。
还要记住:读不到压力文件,不代表没有压力——这个能力需要内核支持,先确认本机是否提供该接口。如果启用了统一层级,每个子目录里也有同格式的三份压力文件,可以按服务层级看。
如果确实读不到:退回到一组替代证据——负载的构成(可运行与不可中断各占多少)、设备侧延迟形态、不可中断进程数。这三样合起来,同样能回答"压力是不是落在 I/O 上"。
四步分诊顺序
- 时间维度:持续劣化还是周期性抖动。周期性抖动要往定时调度、备份窗口、日志切分上找;持续劣化才往容量与泄漏上找。
- 这是不是这台机器自己能解决的:虚拟化下的"CPU 窃取时间"高,说明宿主拿走了一部分时间——此时客户机内的任何调优都不会有效,动作只有改机型或迁移。
反过来也要会排除:窃取时间不高时,如果压力落在 I/O 或内存上,那更可能是本机资源与负载形态的问题——宿主侧不是主要方向,这时才值得在本机继续往下查。 - 压力落在 CPU、内存还是 I/O 哪一处:用刚说的三处压力指标来回答。
- 才做进程归因:谁占用高、谁在等。
判据:每一步都有一个明确的"出口"——第 1 步决定往哪一类原因查,第 2 步决定"要不要在本机折腾",第 3 步决定看哪一处,第 4 步才是找具体进程。跳步的典型后果就是"直接看占用排行",然后朝着错误方向优化。
占用 ≠ 压力
一个进程占用高,不一定造成别人排队;反过来,真正的排队可能由一个占用不高的进程引起——它持有资源,或者自己在等 I/O。
所以正确次序是:先看压力与延迟 → 再与基线对照 → 最后才看用量。
判据:说"有人在等",要有压力与延迟证据,不能只拿占用排行当结论。这两件事经常被混为一谈,代价是你优化了一个很忙的进程,而排队的人还在排队。
只读观测命令组
# 只读观测(负载值、压力数值与核数一律用占位符;全部只读)
# 负载,以及"当前被阻塞、等待 I/O 完成的进程数"
# 三处压力:/proc/pressure/ 下的 cpu、memory、io 三个文件
# some = 至少有一些进程停滞的时间占比;full = 所有非空闲进程同时停滞
# 几个时间窗的均值 + 累计停滞时间(用来抓毛刺)
# 设备侧延迟形态与连接数(与压力取同一时段的基线口径)
# CPU 窃取时间(部分工具在部分内核版本不显示,可直读运行状态接口)
判据:压力接口读不到时,先确认本机是否提供这个能力,而不是直接得出"没有压力"。
现象到动作的判定表
| 现象 | 结论 | 动作 |
|---|---|---|
| 负载高,但 CPU 闲 | 多半是 I/O 或阻塞 | 看"等待 I/O 的进程数"与 I/O 压力 |
| 压力只在 I/O,且"所有进程同时停滞" | 整体被拖住,算力被浪费 | 优先处理 I/O 侧——这是最紧的一档 |
| 压力只在 I/O,“有人停滞” | 局部资源紧张 | 找到在等 I/O 的那部分进程 |
| 持续劣化 | 往容量与泄漏方向查 | 看趋势与容量,而不是逐次重启 |
| 周期性抖动 | 与定时调度、备份窗口、日志切分重合 | 错峰或限流 |
| 窃取时间高 | CPU 侧不归你管 | 改机型或迁移,本机调优无效 |
| 没有基线 | 无法判断什么是"异常" | 先建立最小基线,再谈调参 |
改完怎么验证:每项一条判据
- 同一口径与基线对照有改善:不要跨口径比。
- 压力指标回落:三处里你针对的那一处,some 与 full 都要看。
- 业务侧时延指标同步改善:只看系统指标,可能"系统好看、业务仍慢"。
- 没有为误判买单:没有盲目加 CPU,也没有去改无关参数。
- 回滚路径:改动前记录原值。
- 验证方式固定成一句话:同一口径下压力与业务时延同步改善、且没有误改,才算通过。
这篇不解决什么
- 不处理 I/O 异常分支:讲《云盘变慢或报 I/O 错误时你能做什么》的那篇处理块设备与虚拟盘 I/O 侧的异常分支;本篇是通用分诊——窃取时间两篇都会用到,但结论落点不同。
- 不决定怎么设限:讲《CPU 限制该用权重还是上限》的那篇决定怎么设限;本篇只回答压力在哪、要不要设限。
- 不展开内存限制取舍:cgroup 内存上限与 OOM 取证属于内存维度的限制与取证;本篇只把内存压力作为三处之一。
踩坑清单
- 负载高就说"机器没问题":负载里含不可中断睡眠,CPU 闲不代表没压力。
- 一上来就看占用排行:方向被引到加 CPU 上。
- 把"占用高"当成"有压力":两者不是同一件事。
- 只看"有人在等"的口径,忽略"全都在等":最紧的一档被漏掉。
- 只看均值不看累计值:时延毛刺全被抹平。
- 周期性抖动去加容量:查错了方向。
- 持续劣化当成抖动:一次次重启,趋势越来越差。
- 没有基线就调参:改完也不知道是好是坏。
- 忽略窃取时间:在虚拟机上白调一轮。
- 只看系统指标不看业务侧时延:系统指标都正常,而业务还在超时。
- 压力接口读不到就认为没压力:可能是本机没提供这个能力。
- 把 CPU 那一项的 full 当成判据:它在系统级别上没有定义。
- 点赞
- 收藏
- 关注作者
评论(0)