CPU 只有 30%,系统却慢到不可用?

文章目录

这是我们排查最多的

这是一次非常典型的事故场景。

业务反馈系统响应明显变慢,

用户投诉集中在「接口卡顿」「页面转圈」。

首先打开系统监控进行排查:

复制代码
CPU Usage:      30%
Memory Usage:   55%
Load Average:   0.8
Disk IO:        正常

Grafana指标全都正常。

从资源视角看,系统甚至谈不上有压力。

但事实是------

请求已经慢到用户无法接受。

CPU不高≠系统在"正常工作"

这是很多团队在监控体系上踩的第一个认知坑。

CPU 只代表一件事:

CPU 正在消耗多少计算资源

它完全不能反映:

  • 请求是否被阻塞
  • 线程是否在等待
  • I/O 是否成为瓶颈
  • 业务链路是否已经"局部瘫痪"

一句话总结:

CPU 低,只能说明系统没在算,不代表系统没在等。

CPU 低系统慢,原因何在?

1️⃣线程池被 I/O 阻塞"悄悄吃满"

我们先看一段最常见的 Java 服务结构:

复制代码
@RestController
public Result queryOrder() {
    Order order = orderService.getOrder(id);
    return Result.ok(order);
}

在代码层面,它看起来是同步、顺序、可控的。

但在运行时,真实路径可能是:

HTTP Request

Tomcat Worker Thread

数据库连接池获取连接(阻塞)

执行 SQL(慢查询)

等待下游 RPC 返回

如果其中任意一步变慢:

  • Tomcat 工作线程被占用
  • 新请求开始排队
  • 响应时间指数级上升

而 CPU 使用率呢?

几乎不变。

因为线程大多数时间都在

WAITING / TIMED_WAITING。

2️⃣连接池耗尽,比 CPU 打满更致命

我们在事故中经常看到这样的监控组合:

复制代码
DB CPU:           40%
DB QPS:           正常
Application TPS:  下降

问题在哪?

复制代码
HikariCP Active Connections: 50 / 50
Waiting Threads:             持续增长

这意味着:

  • 数据库还能扛
  • 但应用已经拿不到连接
  • 请求在 getConnection()阶段就被阻塞

从 JVM 角度看:

复制代码
HTTP-8080-exec-123" waiting on condition

系统没有崩溃,

但已经无法提供有效服务。

3️⃣一个慢接口,拖垮整个系统吞吐

很多团队忽略了一个事实:

系统吞吐 ≈ 最慢路径的能力

假设你的接口响应时间从 50ms 变成 300ms:

复制代码
原 QPS ≈ 1000
实际 QPS ≈ 160

CPU 依然很低,

但线程池开始排队,延迟开始堆积。

这类问题的典型特征是:

  • CPU 不高
  • 内存不满
  • 但 P95 / P99 延迟持续升高

如果你只盯着平均值和 CPU,是完全感知不到的。

Java 应用怎么查?

当你确认:

  • CPU 不高
  • 内存正常
  • 但请求明显变慢

第一件事,别再盯 Grafana 了。

你需要直接进入 JVM 内部,看它到底在"忙什么"。

1️⃣ 先看线程:CPU 不高,线程在干嘛?

第一步,永远是线程状态。

复制代码
jstack <pid> > jstack.log

重点不是线程数量,而是状态分布:

复制代码
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING

在"CPU 低但系统慢"的事故中,我们最常看到的是:

  • RUNNABLE 很少
  • WAITING / TIMED_WAITING 占大多数

典型线程栈长这样:

复制代码
"HTTP-8080-exec-124" prio=5 tid=0x00007f8c940 waiting
    at java.util.concurrent.locks.LockSupport.park()
    at java.util.concurrent.FutureTask.get()

这说明什么?

线程没有在算,而是在等结果。

等什么?

  • 数据库返回
  • 下游 RPC
  • 锁释放
  • 线程池资源

2️⃣ 看线程池:不是没线程,是用不上线程

很多团队只关心线程池大小,却不看运行状态。

如果你用的是 ThreadPoolExecutor,重点看这几个指标:

复制代码
activeCount 
queueSize 
completedTaskCount

一个非常危险的组合是:

复制代码
activeCount ≈ maxPoolSize
queueSize 持续增长

这意味着:

  • 线程已经被慢任务占满
  • 新请求只能排队
  • 延迟开始指数级放大

而 CPU?

依然不高。

3️⃣ 再看 GC:不是 Full GC,但"轻微抖动"很要命

很多人一看到系统慢,就下意识否定 GC:

"没有 Full GC,应该不是 GC 问题。"

但真实情况是:

  • 频繁 Young GC
  • Stop The World 很短,但次数极多

你会在 GC 日志里看到类似:

复制代码
[GC (Allocation Failure) 256M->128M(512M), 15ms]

15ms 不长,但如果:

复制代码
每秒 20 次

那对延迟型服务来说,就是灾难。

尤其是:

  • 接口本身就慢
  • 请求已经在排队

GC 抖动会直接放大用户感知延迟。

4️⃣ 堆没满,但对象"活得太久"

这是非常容易被忽略的一点。

复制代码
> jmap -histo <pid> | head -20

你可能会看到:

复制代码
num     
#instances
    
#bytes
  class name 
--------------------------------------- 
1:      8,000,000     640MB   byte[] 
2:      2,300,000     184MB   java.lang.String

这说明:

  • 对象在堆里大量堆积
  • GC 清不掉
  • 线程在分配内存时越来越慢

CPU 不高,

但 JVM 已经开始效率衰减。

5️⃣ 最后看一个致命点:同步与锁

如果线程栈里频繁出现:

复制代码
java.lang.Object.wait() 
java.util.concurrent.locks.AbstractQueuedSynchronizer

那你基本可以确认:

系统慢,不是因为算得慢,而是锁抢不过来。

这类问题的特点是:

  • CPU 利用率低
  • 吞吐下降明显
  • 延迟突然拉长

而且,扩容几乎无效。

Prometheus + Grafana 为啥看不出问题?

因为大多数监控只做了资源观测,没有做系统行为观测。

常见指标是:

复制代码
node_cpu_seconds_total 
node_memory_MemAvailable_bytes

但真正该关注的,是这些:

复制代码
http_server_requests_seconds_bucket 
jvm_threads_state{state="BLOCKED"} 
hikaricp_connections_active 
mysql_global_status_threads_running

如果你没有:

  • 接口分位延迟(P95 / P99)
  • 线程池状态
  • 连接池使用情况
  • 关键依赖的响应时间

那么监控只能告诉你一句话:

"服务器还活着。"

但业务是否健康,它不知道。

中小团队常忽视的"慢性事故"

我们复盘过大量事故后发现:

这类问题很少第一时间报警

通常是用户先感知

再由人肉排查发现

原因只有一个:

监控体系没有覆盖"用户体验劣化"的早期信号

等到 CPU 真正升高时,

系统往往已经处在雪崩边缘。

一个更靠谱的判断逻辑

与其问:

"CPU 高不高?"

不如问这三个问题:

  1. 请求在系统中卡在哪一层?
  2. 哪个资源正在成为隐形瓶颈?
  3. 如果现在继续变慢,谁能第一时间发现?

真正成熟的运维体系,

不是等系统挂了再报警,

而是能在**"慢"刚开始出现时就介入**。

写在最后

CPU 只有 30%,系统却慢到不可用,

从来不是一个偶发问题。

它往往意味着:

  • 系统已经进入亚健康状态
  • 只是还没触发致命阈值

真正的分水岭,不在于是否出过事故,而在于:

系统开始变慢的那一刻, 你能不能看见?

所以、单纯的监控系统层面的cpu、内存、磁盘等等,

是远远不够的。

线程在等什么?

连接池还有多少空闲?

GC 暂停是否隐形拖垮了延迟?

数据库/Redis 调用是否在异常?

只有把以下这些 JVM 核心亚健康指标

实时采集、可视化、设置阈值告警,

你才能在"页面刚开始卡"而不是"系统彻底挂"的时候发现问题。

相关推荐
爱吃生蚝的于勒2 小时前
【Linux】网络之http协议
linux·运维·服务器·网络·数据结构·c++·http
蜜獾云2 小时前
Elastic Stack基础概念
运维·jenkins
wanhengidc2 小时前
企业如何有效利用高防服务器
运维·服务器·网络·安全·web安全·智能手机
嵌入式学不会不改名2 小时前
windSurf远程连接ssh过慢问题
运维·ssh
F1FJJ2 小时前
开源实践:用 Go 实现浏览器直连内网 RDP/SSH/VNC
运维·网络·网络协议·网络安全·golang·ssh
天空属于哈夫克32 小时前
企微私域自动化:从手动拉群到全流程无人值守的技术实现
运维·自动化·企业微信
集智飞行2 小时前
禁用Ubuntu网卡的电源管理(Power Management)
linux·运维·ubuntu
可涵不会debug2 小时前
五种IO模型
运维·服务器·网络
水木兰亭2 小时前
多进程编程总结
linux·运维·服务器