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 核心亚健康指标

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

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

相关推荐
XIAOHEZIcode14 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220701 天前
如何搭建本地yum源(上)
运维
大树884 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质4 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工4 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智4 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_4 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉4 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦4 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw