压测 TPS 卡在 800CPU 只跑四成六步定位法

TPS 卡在 800 不是 CPU 的锅:Hikari 连接池只给了 50 个连接、超时还没配,几百个请求全堵在 getConnection()。改完 1600。

先看那条骗人的 CPU 曲线

场景没什么稀奇:核心接口单压,并发从 100 加到 200,TPS 涨到 800 就躺平。再加到 300,不涨反跌。

看监控,CPU 只跑到四成。第一反应自然是"机器还没吃满,加并发就完事了"。把线程数翻倍,TPS 掉到 700 出头。

CPU 不满,说明瓶颈根本不在计算层。计算层长什么样?CPU 打满、GC 频繁、上下文切换爆炸。CPU 闲着的卡顿,八成是在等------等锁、等连接、等 IO、等下游。

记住一句话:别盯计算层,分层往下摸。应用层、中间件层、数据库层,一层一层排。

六步,一步都别省

排查流程固定成六步,少讲一步面试官就当你没做过:

flowchart TD A[现象] -->|定位起点| B[工具] B -->|采集证据| C[分析] C -->|锁定层| D[根因] D -->|给出改法| E[方案] E -->|复测对比| F[数据] F -->|没解决| B

顺序不能乱。现象是"TPS 卡 800、CPU 四成";工具拿来看证据;分析把证据收敛到某一层;根因是那一层里具体哪行配置;方案得可执行;数据是改完的对比。

面试里最容易漏掉的是最后一步,数据。说"我改了连接池"没说服力,说"TPS 从 800 到 1600"才叫做过。

为什么第一把工具是 netstat 而不是 jstack

先用 netstat(或 ss)看连接状态分布:

bash 复制代码
netstat -n -p tcp | awk '{print $$6}' | sort | uniq -c | sort -rn

输出大概长这样:

text 复制代码
   812 TIME_WAIT
   634 ESTABLISHED
   287 CLOSE_WAIT

满屏 TIME_WAIT,大量连接挤在同一条道上排队。方向当场锁死:问题在连接层,不在计算层。

这里有个容易搞混的点。TIME_WAIT 多,说明连接反复建立销毁、没有复用;CLOSE_WAIT 多,是应用侧没关连接,通常意味着连接泄漏。两者指向的原因完全不同,别混着说。

根因:50 个连接接 200 个并发

顺着连接层往下,查数据库连接池配置:

yaml 复制代码
hikari:
  maximum-pool-size: 50
  connection-timeout: 0   # 没配超时

最大连接数只给 50,超时也没配。并发爬到 200 以上,几百个请求全在池子外面排队等连接。每个请求平均等几十上百毫秒,TPS 上不去,CPU 在旁边闲着看戏。

这就是典型的"CPU 不满但吞吐上不去":线程全阻塞在 getConnection(),一点计算量都没有。

改法很直接:

yaml 复制代码
spring:
  datasource:
    hikari:
      maximum-pool-size: 200
      minimum-idle: 50
      connection-timeout: 3000
      idle-timeout: 600000
      max-lifetime: 1800000

connection-timeout 必须明确给值。给了超时,池子撑不住时会快速失败返回,而不是把请求无限挂在那儿------快速失败比慢慢等死好排查得多。

复测:800 → 1600

同一个脚本、同一台机器、同样并发 200,重压一次:

指标 修复前 修复后
TPS 800 1600
P99 响应时间 480ms 210ms
CPU 使用率 40% 68%
连接池等待 大量排队 基本为零

TPS 翻倍,CPU 从四成涨到接近七成。这时候 CPU 涨上来是好事,说明请求终于被真正处理,而不是堵在池子门口。

面试官想听的就是这组前后对比数字。

两个高频变体

变体一:响应时间从 200ms 飙到 800ms,TPS 却正常。

还是那套分层思路,但先上链路追踪,别急着改代码。查出来是一条慢 SQL 在 500 万行的表上做全表扫描,加个联合索引,响应时间降到 80ms 以内。

迷惑点在于 TPS 没掉。TPS 正常说明吞吐没受影响,慢的是单请求耗时,方向该往"单条查询效率"走,跟连接、并发没关系。

变体二:CPU 跑满 100%,接口超时。

top -Hp <pid> 找出吃 CPU 最高的线程 ID,转十六进制,再 jstack <pid> | grep -A 30 <hex> 定位栈帧。常见结果是一段复杂正则或弹性回缩逻辑在高并发下被反复打爆。优化那段正则,CPU 降到 30% 以下。

top + jstack 是 CPU 打满场景的核心工具链,配上线程 ID 转十六进制,基本能追到具体代码行。

三句话版本

现象定起点,工具取证,分析锁层,根因落到具体配置或代码,方案可执行,数据做对比。

面试回答六步一步别省。最值钱的永远是最后那步------用前后对比数据把"我真的做过"砸出来。

写在最后

压测调优拼的不是工具多,是分层排查的习惯。CPU 不满的时候,你第一反应是加机器还是查连接池?这两个方向之间往往差着整整一倍 TPS。

顺手的赞,点一下就行。

相关推荐
晚安日记wanna1 小时前
批量请求失败只弹一个 Toast面试官想听五层
前端·面试·架构
晚安日记wanna1 小时前
TS const 类型参数一道面试题的四层追问
前端·面试·typescript
晚安日记wanna1 小时前
大表 DDL 面试翻车现场Online DDL 为什么还会锁死业务
数据库·面试·架构
晚安日记wanna1 小时前
MySQL 主从延迟别只答并行复制
数据库·面试·架构
SamDeepThinking1 小时前
Java 17内存屏障:为什么需要,怎么用
java·后端·面试
CC数分2 小时前
2026年金融数据分析岗位需要哪些工具?2027届秋招备考指南
面试·数据分析
吴声子夜歌2 小时前
Shell编程实例——bash入门
linux·运维·shell
爱和冰阔落3 小时前
【Linux】条件变量为什么必须配合互斥锁:从 pthread_cond_wait 到阻塞队列
linux·运维·c++·缓存·中间件·安卓
ynchyong3 小时前
Linux nohup 后台服务合并标准错误输出到一个文件
java·linux·运维