性能测试:如何分析 CPU、内存、磁盘、网络瓶颈

性能测试里最常见、也最容易出错的一类结论,就是这种:

  • CPU 高,所以瓶颈在 CPU
  • 内存高,所以瓶颈在内存
  • 磁盘 util 高,所以瓶颈在磁盘

这类说法不是绝对错,但通常太快了。
因为资源“忙”不等于资源已经成为系统主瓶颈。

从真实排障经验看,资源瓶颈分析真正难的不是看监控,而是要回答:

  • 这个资源的异常变化,是否真的开始限制系统继续产出
  • 它是根因,还是被动结果
  • 它和吞吐、尾延迟、错误率之间有没有明确的时间对应关系

所以做资源分析时,最先警惕的不是“某个指标高”,而是“我是不是下结论下得太快了”。

一、CPU 分析不能只看使用率,要先看“它在忙什么”

看 CPU,基本只看总使用率。
这远远不够。

通常至少会同时看:

  • user
  • system
  • iowait
  • load average
  • 上下文切换
  • 线程数

因为这些指标组合起来,才能大致判断 CPU 为什么高。

1. user 高,通常更像业务逻辑或计算消耗

常见于:

  • JSON 序列化反序列化
  • 加解密
  • 复杂计算
  • 代码热路径问题

2. system 高,通常要看内核态消耗

常见于:

  • 网络包处理
  • 系统调用频繁
  • 锁竞争激烈

3. iowait 高,通常不能简单说“CPU 高”

这类情况更常见的解释是:

  • CPU 没真正忙在算
  • 而是在等磁盘或网络 I/O

所以如果只看总 CPU,会把很多 I/O 问题误判成 CPU 问题。

二、CPU 真正成为瓶颈时,通常会伴随几个联动信号

更愿意把 CPU 瓶颈理解为:
CPU 已经成为继续提升吞吐的主要限制因素。

比较典型的联动信号是:

  • CPU 接近上限
  • 吞吐不再明显上升
  • P95 / P99 开始明显恶化
  • 线程排队增加
  • 错误率开始抬头

如果只有 CPU 高,但吞吐还在健康增长、延迟也没明显变差,那更像是资源被充分利用,而不是主瓶颈。

三、内存分析真正要分清楚“占用高”和“回收失控”

内存问题是另一类极容易误判的地方。

看到内存高,直觉上很容易觉得系统有问题。
但很多服务本来就应该把空闲内存拿去做:

  • 缓存
  • page cache
  • 对象复用

所以光看内存占用高不高,信息量很低。

通常更关注下面这些问题。

1. 内存是不是持续单向上涨

如果在长稳压里出现:

  • 请求结束后不回落
  • 周期性越涨越高

那就要怀疑:

  • 泄漏
  • 对象滞留
  • 连接不释放

2. GC 是否明显恶化

尤其在 JVM 体系里,内存问题很多时候最先暴露在:

  • GC 次数上升
  • Full GC 出现
  • GC 暂停时间变长

如果 GC 已经明显影响响应时间,那内存问题就不再只是“资源状态”,而已经开始影响业务体验。

3. 堆内、堆外、缓存是否混在一起看

内存分析不够细,只看一个总数。
但真实问题里,堆外内存、页缓存、直接内存、消息缓冲都有可能是关键因素。

四、磁盘瓶颈最容易被低估,因为很多系统不是“打满”,而是“开始等”

磁盘问题不一定表现成非常夸张的写满。
更常见的是它开始拖慢系统,但表面上还没完全打满。

通常会重点看:

  • iops
  • throughput
  • await
  • util
  • 队列长度

1. await 往往比 util 更有解释力

很多时候 util 已经高了,但真正更说明问题的是 await。
因为 await 上升意味着请求已经开始明显等待磁盘。

2. 写放大和日志 I/O 经常被忽略

项目里非常常见的一种情况是:

  • 应用本身业务逻辑不算重
  • 但日志刷盘、数据库写入、临时文件落盘叠加后
  • 磁盘侧开始明显抖动

这种场景下,如果不把日志和业务写 I/O 拆开看,很容易只看到“系统整体慢”,但找不到真正的触发点。

五、网络瓶颈很多时候不是“带宽打满”,而是链路质量变差

网络问题最容易被简化成一句:

  • 带宽够不够

但真实系统里,网络瓶颈常常表现得更隐蔽。

更常看的信号包括:

  • RTT
  • 重传
  • 丢包
  • 连接数
  • NAT / LB 行为
  • 网卡队列

1. 带宽没满,不代表网络没问题

例如:

  • RTT 明显升高
  • 重传增多
  • 短连接过多
  • 中间 LB 或防火墙先顶住

这些问题都可能导致系统延迟显著变差,而带宽图本身看起来还不夸张。

2. 网络问题很容易伪装成应用超时

项目里很常见的一种现象是:

  • 接口开始超时
  • 最常见的直觉判断是应用变慢

但真实根因可能是:

  • 下游链路抖动
  • 跨机房 RTT 上升
  • 中间代理层排队

如果没有网络侧证据,很容易误把网络问题追到业务代码上。

六、资源瓶颈分析里最有价值的,不是看单机,而是看因果顺序

在项目里最常做的一件事,就是对齐时间窗口,看谁先变差。

例如一次压测里,如果顺序是:

  1. 磁盘 await 先升高
  2. 随后数据库响应时间抬高
  3. 然后应用线程开始堆积
  4. 最后接口 P99 恶化

那问题链路就比较清楚了。
反过来,如果是:

  1. 应用 CPU 先顶住
  2. 然后吞吐停止增长
  3. 接着错误率抬头

那解释方向就完全不同。

所以资源分析不能只盯最终状态截图,要尽量看变化顺序。

七、在项目里最常见的几个资源误判

误判 1:CPU 高就一定是 CPU 瓶颈

很多时候只是资源利用率高,或者在等 I/O。

误判 2:内存高就要先扩容

如果只是缓存吃满空闲内存,扩容未必是最优先动作。

误判 3:磁盘 util 高就一定是主因

要看它是不是和延迟恶化同步出现,还是只是跟随性波动。

误判 4:网络没打满就说明没问题

很多网络问题根本不是靠带宽图发现的。

八、更常用的资源瓶颈分析顺序

1. 先看吞吐和延迟拐点

先找到系统开始退化的时间窗口。

2. 再在同一时间窗口对资源图做对齐

看:

  • CPU
  • 内存
  • 磁盘
  • 网络

九、如果现场让我只带几条命令,通常会带这些

资源分析不能只靠监控大盘。
一旦进到现场排查,通常会补几组系统命令,把“图上的异常”落到机器视角。

例如 Linux 机器上,我最常用的是:

1
2
3
4
5
6
mpstat -P ALL 1
pidstat -p <pid> -u -r -d 1
iostat -x 1
sar -n DEV 1
ss -s
top -H -p <pid>

这些命令各自回答的问题很明确:

  • mpstat 看 CPU 各核和 iowait
  • pidstat 看进程级 CPU、内存、I/O
  • iostat 看磁盘 awaitutil
  • sar -n DEV 看网卡流量和错误
  • ss -s 看连接堆积和 socket 状态
  • top -H 看线程级热点

如果图上已经出现退化,这几条命令通常能帮你确认它到底落在哪一层。

十、在项目里常用的一套资源排查记录法

为了避免现场只剩零散截图,通常会同步记一张排查表:

时间窗口 现象 CPU 内存 磁盘 网络 初步判断
20:10-20:15 P99 抬高 user 82% 稳定 await 低 正常 偏 CPU 热点
20:16-20:20 吞吐停滞 user 78% 稳定 await 升高 正常 磁盘等待开始出现
20:21-20:25 错误率上升 user 76% 稳定 await 高 正常 磁盘成为主限制

这张表最大的作用,是强迫自己按时间顺序解释问题,而不是看到最后一张图才倒推原因。

十一、一次典型排查里怎么判断根因和伴生现象

例如现场看到:

  • CPU 80%
  • P99 变差
  • 磁盘 await 明显上升

这时候不能立刻下结论说“CPU 是瓶颈”。
通常会继续追两个问题:

  1. CPU 高是在问题前出现,还是在问题后出现
  2. 吞吐停滞更早跟谁同步,CPU 还是磁盘等待

如果顺序是:

  1. 磁盘 await 先升
  2. 数据库响应变慢
  3. 应用线程开始堆积
  4. CPU 才跟着升

那 CPU 更像伴生现象,不该当成主因去优化。

谁最先出现异常变化。

3. 再看更细一级的证据

例如:

  • JVM / GC
  • SQL 等待
  • Redis 延迟
  • 网卡重传
  • 磁盘 await

4. 最后再下瓶颈结论

这样结论会比“看一张资源图就拍板”稳很多。

结语

资源瓶颈分析最怕的,就是把“谁高”直接等同于“谁有罪”。
真正有效的判断,应该建立在三件事上:

  • 这个资源是否开始限制系统继续产出
  • 它和吞吐、延迟、错误率之间有没有明确联动
  • 它是根因,还是被动结果

只有把这些关系理顺,CPU、内存、磁盘、网络的分析才真正有排障价值。