最近用ChatGPT、Codex分析接口性能时,有一类问题特别容易把排查方向带偏:
接口越来越慢,但CPU一点都不高。
比如线上监控显示:
CPU:28%
内存:62%
接口 P95:4.8s
第一眼看过去很奇怪。
如果机器没有算满,为什么接口还能慢到几秒?
很多人看到这种情况,会继续盯:
CPU。
GC。
服务器配置。
甚至直接考虑扩容。
但真实情况往往是:
接口不是“算得慢”,而是在“等”。
它可能在等:
数据库连接。
线程池。
锁。
下游服务。
消息队列。
磁盘IO。
网络响应。
所以一个接口真正的响应时间,其实可以简单理解成:
执行时间 + 等待时间
如果真正执行代码只用了500ms,但等待资源花了4秒,
CPU当然不会很高。
接口却一样很慢。
一、为什么CPU低,接口仍然可以非常慢?
假设一个请求的完整过程是:
进入接口
↓
等待数据库连接 1.5s
↓
执行SQL 200ms
↓
等待下游服务 1.8s
↓
业务计算 100ms
↓
返回
真正消耗CPU的部分可能只有几百毫秒。
但用户看到的是:
接近4秒。
所以CPU利用率低只能说明:
机器没有持续做大量计算。
它不能证明:
请求没有被阻塞。
这也是性能问题里一个很重要的区别:
CPU Bound
和:
Wait Bound
CPU Bound是:
机器一直在算。
Wait Bound则是:
大量请求都在等资源。
后者在线上系统里其实非常常见。
二、最常见的第一种等待:数据库连接池排队
假设数据库连接池最多只有:
20 connections
平时并发不高,完全够用。
但流量增加以后,同时来了100个请求。
前20个请求拿到连接。
后面的80个请求只能:
等。
这时候你看数据库SQL可能会发现:
每条查询其实只有:
80ms
并不慢。
但接口耗时却变成:
3s
真正慢的是:
getConnection()
之前的等待时间。
所以只看慢SQL,很容易误判。
真正应该同时看:
连接池当前使用数。
最大连接数。
等待队列。
获取连接耗时。
如果连接池长期打满,
CPU可能仍然只有30%。
因为大量线程根本没有在计算。
它们只是在:
等待连接。
三、第二种常见等待:线程池已经塞满
很多Web服务、异步任务和RPC调用都依赖线程池。
假设:
worker threads = 32
流量继续增加以后,
32个线程都在处理慢请求。
后续请求只能先进入队列。
于是请求真正开始执行之前,就已经等了1秒、2秒甚至更久。
这时候系统可能表现为:
吞吐没有明显增加。
接口延迟持续上涨。
CPU却没有打满。
原因很简单:
线程不是在算,而是在等待其他资源。
特别是线程内部又在等待:
数据库。
网络。
外部API。
那整个系统会形成:
线程占着不释放,
新请求继续排队。
最后延迟越来越高。
四、第三种等待:锁竞争
再看一个很典型的情况。
代码里有一个共享资源:
synchronized
或者:
Mutex。
数据库行锁。
分布式锁。
平时并发低时:
几乎感觉不到。
但并发上来以后,
很多请求会同时竞争同一把锁。
只有一个请求可以继续。
其他请求全部:
WAITING。
于是你会看到:
CPU不高。
数据库也不一定特别忙。
但接口P95、P99一路上涨。
因为真正的瓶颈不是计算能力。
而是:
Serialization——被迫串行化
表面上系统有几十个线程。
实际上某个关键路径一次只能过一个请求。
五、第四种等待:下游服务变慢
很多接口自己非常简单。
例如:
用户请求
↓
订单服务
↓
库存服务
↓
支付服务
↓
返回
订单服务本身可能只执行:
50ms。
但库存服务突然需要:
1.5s。
支付服务又需要:
2s。
最后用户看到:
3.5秒以上。
这时候如果只看订单服务CPU:
很可能只有:
25%
因为它大量时间都在等:
网络响应。
所以分布式系统里判断接口慢,不能只看:
当前服务。
还需要看完整:
Request Trace
到底哪一段耗时最大。
六、为什么ChatGPT、Codex也容易被CPU指标带偏?
如果直接告诉Agent:
接口很慢,CPU只有30%。
它很容易开始分析:
服务器配置。
线程数。
GC。
代码复杂度。
这些方向不一定错。
但更有效的输入应该是:
P95 = 4.8s
CPU = 30%
DB query = 150ms
Connection wait = 1.6s
Downstream call = 2.4s
这时候Agent很快就会发现:
真正的问题不是:
计算太慢。
而是:
等待时间太长。
所以性能分析里,最好不要只告诉Codex:
“系统慢。”
而要尽量把请求时间拆出来。
七、真正应该看的不是一个总耗时,而是时间都花在哪里
比如一个5秒请求:
线程池排队:800ms
获取数据库连接:1200ms
执行SQL:300ms
调用下游:2300ms
业务计算:200ms
其他:200ms
这时候真正CPU执行的部分可能只有:
几百毫秒。
大部分时间都消耗在:
等待。
所以性能优化最重要的一步往往不是:
直接改代码。
而是先做:
Latency Breakdown——延迟拆解
你要知道:
总共5秒。
到底哪3秒、4秒花在什么地方。
只有这样才能决定:
应该扩容。
调连接池。
减少锁。
优化下游。
还是改代码。
八、为什么P95、P99比平均值更重要?
假设平均响应时间:
600ms
看起来还不错。
但P95:
4.5s
P99:
8.2s
说明少量请求正在经历非常严重的等待。
这种情况很可能和:
资源竞争。
队列。
连接池。
锁。
有关。
因为平均值会把这些慢请求稀释掉。
所以如果你遇到:
“有些用户觉得特别慢,但平均指标还行”
更应该看:
P95。
P99。
Queue Time。
Wait Time。
而不是只看Avg Latency。
九、怎么快速判断是不是“等待型瓶颈”?
可以先看几个信号。
CPU低,但并发一高延迟就上涨
很典型。
单条SQL不慢,但数据库连接池经常满
说明卡在拿连接。
Thread Dump里大量WAITING / BLOCKED
说明线程在等。
Trace里某个下游Span特别长
说明时间花在外部调用。
请求量增加后,吞吐不上升但Queue变长
说明系统已经进入排队状态。
这些信息结合起来,通常比单独盯CPU更有价值。
十、优化时不要第一反应就是“把池子调大”
比如连接池满了。
最直接的做法是:
20 → 100
线程池不够:
32 → 128
短期可能有效。
但不一定真正解决问题。
因为如果数据库本身只能稳定处理20个高并发连接,
你把连接池放大到100,
结果可能只是:
数据库更慢。
锁竞争更严重。
最终所有请求一起变慢。
所以真正应该先问:
为什么一个请求占用这个资源这么久?
例如:
事务是不是太长。
SQL是不是一次拉太多数据。
下游Timeout是不是过高。
线程是不是在同步等待外部结果。
池大小只是容量问题。
资源持有时间才往往是根。
十一、一个特别实用的指标:等待时间占比
这篇我建议只看一个核心指标:
Wait Time Ratio——等待时间占比
计算方法很简单:
请求等待资源的时间 ÷ 总响应时间
例如一个接口总耗时:
5s
其中:
数据库连接等待1.2s。
线程队列等待800ms。
下游网络等待2.2s。
总等待:
4.2s
那么:
等待时间占比 = 84%
这说明真正用于:
业务计算、SQL执行、数据处理
的时间其实非常少。
这时候继续优化CPU计算代码,
意义就很有限。
十二、等待时间占比高,应该优先查什么?
如果长期高于60%:
优先看:
数据库连接池。
线程池队列。
锁竞争。
下游接口。
IO。
消息队列积压。
Timeout和Retry。
如果在30%—60%:
说明既有等待,也有计算成本。
应该进一步拆:
哪个等待项最大。
如果长期低于30%,CPU又持续高:
那才更像真正的:
CPU Bound。
这时候再去优化算法、序列化、计算逻辑会更合理。
十三、怎么降低等待时间?
最有效的方向通常不是一个。
第一,缩短资源持有时间
例如数据库事务不要包太多外部调用。
连接拿到以后尽快释放。
第二,减少不必要的同步等待
能并行的下游调用,不一定必须串行。
能异步的任务,不一定全部阻塞用户请求。
第三,给队列设置合理上限
不是无限堆积。
超过系统容量以后,应当限流、降级或者快速失败。
第四,让Timeout和Retry有边界
一个下游已经很慢,
Agent或者程序还连续Retry,
只会进一步占用线程和连接。
第五,用Trace验证优化结果
不要只看:
“CPU下降了。”
真正要看的是:
P95、P99和等待时间有没有下降。
十四、Plus和Pro怎么判断?
如果你的等待时间占比还很高:
接口慢主要来自:
连接池。
线程池。
锁。
下游网络。
队列。
那当前真正限制效率的不是ChatGPT、Codex容量。
而是:
系统资源调度和等待链路还没有稳定。
这种阶段Plus通常已经够用。
更值得先完善:
Trace。
连接池监控。
线程队列。
Timeout。
锁竞争分析。
资源释放。
否则增加更多AI容量,
只是让Agent更快地产生更多优化建议,
但真正瓶颈仍然卡在运行时资源上。
如果你的等待时间占比已经比较低:
系统延迟结构清楚。
资源池稳定。
P95、P99可控。
下游调用也有明确边界。
同时仍然存在:
大量复杂性能分析任务。
多个Agent持续处理工程问题。
AI容量才真正开始成为瓶颈。
这时候Pro才更容易放大效率。
因为Agent面对的是:
一个已经可测、可定位、可验证的性能体系。
最后
CPU不高,
并不代表系统不忙。
很多时候真正发生的是:
机器没在算,但请求一直在等。
等数据库连接。
等线程。
等锁。
等下游。
等队列。
所以以后让ChatGPT、Codex分析性能问题时,
不要只问:
“为什么CPU只有30%?”
更应该问:
“这5秒里,到底有多少时间是在等待?”
有时候真正拖慢一个接口的,
不是计算太多。
而是:
什么都没做,却等了太久。
长期深度使用各类代码大模型,这里主要分享 ChatGPT、Codex、大模型开发与 AI 编程实战,也会记录实际开发中遇到的性能排查、Agent Workflow 和各类工程问题;对 ChatGPT Plus / Pro 在不同开发强度下的使用差异也比较熟悉,同时把自己一直在用的订阅渠道整理了出来,有需要可以自行参考。
网硕互联帮助中心

![[AI工程] Spring AI第二篇: 2.0 快速接入 DeepSeek、阿里百炼与 Ollama-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/09/20260919065653-6aae32352220e-220x150.png)




评论前必须登录!
注册