【压测】压测出现的问题剖析
核心思路:将系统视为一个整体
压测的本质是给系统一个持续的、高强度的负载,观察其反应。基础指标是系统的“生命体征”,它们的变化揭示了系统内部的处理能力和资源瓶颈。我们的目标是找到那个最先达到极限的“最短木板”。问题现象 -> 核心指标分析 -> 根因定位 -> 排查命令/思路
一、CPU 相关:计算与调度瓶颈
1. 现象:CPU 使用率持续高位(>80%)
-
核心指标:
%us(用户态CPU使用率):高%sy(系统态CPU使用率):可能也高Load Average(系统负载):通常也很高Context Switch/s(上下文切换次数):可能很高
-
详细问题分析:
-
问题A:应用代码计算瓶颈
- 描述:你的业务逻辑本身非常复杂,例如大量的数学计算、复杂的序列化/反序列化(如JSON/Protobuf)、正则表达式匹配、加密解密等。
- 根因:代码中存在性能热点,单个请求就需要消耗大量CPU时间。
- 排查方向:
- 使用性能剖析工具:这是最直接的方法。
- Java:使用
arthas的profiler命令,或async-profiler生成火焰图,直接看到代码热点。 - Go:使用
pprof进行CPU剖析。 - 通用:
perf(Linux)。
- Java:使用
- 检查算法复杂度:是否存在O(n²)或更糟的算法?数据量增大时,CPU消耗是否呈指数级增长?
- 使用性能剖析工具:这是最直接的方法。
-
问题B:垃圾回收(GC)压力
- 描述:主要发生在JVM(Java)、Go(虽然Go的GC高效,但压力大时也会出现)、.NET等有自动内存管理的语言上。
- 根因:短时间内产生大量短生命周期对象,导致GC频繁启动,而GC本身是CPU密集型的操作。
- 指标特征:CPU使用率曲线呈“锯齿状”,内存使用曲线也可能呈锯齿状。JVM的GC日志会显示频繁的Young GC或Full GC。
- 排查方向:
- 查看GC日志:开启JVM的GC日志,分析GC频率、暂停时间。
- 监控堆内存:观察Eden区、Survivor区、老年代的内存变化。
- 优化代码:减少不必要的对象创建,使用对象池。
-
问题C:锁竞争激烈
- 描述:多线程环境下,大量线程在竞争同一把锁,导致大多数线程处于等待状态,而不是执行任务。
- 根因:锁的粒度太粗,或者存在热点资源(如一个全局计数器)。
- 指标特征:
%sys可能会比较高,因为锁的维护需要内核介入。Context Switch/s和Involuntary Context Switches/s(非自愿上下文切换)会非常高。但QPS可能很低,因为CPU时间都花在线程调度和等待上了。 - 排查方向:
- 使用线程分析工具:
jstack(Java)查看线程栈,会发现大量线程阻塞在同一个锁上。 - 优化锁策略:使用细粒度锁、无锁数据结构(如CAS)、或并发容器。
- 使用线程分析工具:
-
2. 现象:CPU 使用率低,但系统负载(Load Average)极高
-
核心指标:
%us和%sy低Load Average远高于CPU核心数(例如4核机器,负载达到20+)%wa(I/O等待CPU时间百分比):高
-
详细问题分析:
- 问题:I/O 瓶颈(最常见)
- 描述:进程不占用CPU是因为它们大部分时间都在等待慢速的I/O操作(磁盘、网络)完成。这些等待的进程/线程处于可运行队列中,推高了负载。
- 根因:见下文【磁盘I/O】和【网络I/O】章节。CPU空闲是因为“工人们”都在排队等“原材料”(数据)到来。
- 排查方向:立即检查磁盘和网络指标。
- 问题:I/O 瓶颈(最常见)
二、内存相关:内存与交换分区瓶颈
1. 现象:内存使用率线性增长直至耗尽(OOM)
-
核心指标:
Used Memory持续增长,Available Memory趋近于0。- 【容器环境】容器内存使用量接近其
limit。
-
详细问题分析:
-
问题A:内存泄漏
- 描述:应用代码中,被分配的内存由于编程错误(如全局Map未清理、监听器未注销等)而无法被垃圾回收器回收。随着时间推移,内存被逐步啃食殆尽。
- 根因:代码Bug。
- 排查方向:
- 生成堆转储:在内存快满时,使用
jmap(Java)或类似工具生成堆转储文件。 - 使用内存分析工具:用MAT、JProfiler等工具分析堆转储,找出占用内存最大的对象和其引用链,定位泄漏点。
- 生成堆转储:在内存快满时,使用
-
问题B:缓存失控
- 描述:本地缓存(如Guava Cache、Caffeine)没有设置合理的容量上限或过期时间,导致缓存数据无限增长。
- 根因:配置不当或缓存策略有误。
- 排查方向:检查缓存配置,设置
maximumSize和expireAfterWrite/Access。
-
2. 现象:Swap 使用量显著增加
-
核心指标:
Swap Used增加- 磁盘I/O写入量 增加(因为Swap在磁盘上)
- 系统响应时间急剧上升且不稳定
-
详细问题分析:
- 问题:物理内存不足
- 描述:当物理内存不足时,操作系统会将物理内存中不活跃的页面换出到磁盘的Swap区域,以腾出空间。
- 影响:一旦某个被换出的内存页需要被访问,系统会产生一个“缺页中断”,并从磁盘Swap区将其换入。这个磁盘I/O操作比内存访问慢几个数量级,导致性能雪崩式下降。
- 排查方向:同【现象1】,解决内存泄漏或优化内存使用。在生产环境中,通常建议禁用Swap,因为其带来的性能抖动是不可接受的。
- 问题:物理内存不足
三、磁盘 I/O 相关:持久化瓶颈
1. 现象:磁盘利用率 100%
-
核心指标:
%util:100%await(平均I/O等待时间):非常高svctm(平均I/O服务时间):可能很高r/s,w/s(IOPS) 或rMB/s,wMB/s(吞吐量) 达到磁盘上限
-
详细问题分析:
- 问题A:日志输出过于频繁
- 描述:应用在DEBUG或INFO级别下打印了过多日志,且日志文件没有配置合理的滚动策略(Rolling Policy)。
- 根因:日志配置不当,或代码中存在循环内打日志等不良实践。
- 问题B:数据库压力大
- 描述:数据库正在进行大量写操作(如批量插入、更新)或读操作(全表扫描),且磁盘性能(特别是IOPS)是瓶颈。
- 根因:SQL查询未优化、索引缺失、缓冲区配置不合理。
- 问题C:应用本身频繁写文件
- 描述:如图片/文件上传服务、数据导出服务等。
- 问题A:日志输出过于频繁
-
排查命令:
iostat -x 1,iotop
四、网络 I/O 相关:通信瓶颈
1. 现象:网络带宽达到网卡上限
-
核心指标:
rxkB/s或txkB/s接近网卡理论速度(如千兆网卡约115MB/s)。drop(丢包数)可能增加。
-
详细问题分析:
- 问题:应用本身就是高带宽类型的,如视频流、大文件传输。
- 排查方向:优化数据传输(如压缩)、使用更高带宽的网络。
2. 现象:TCP 连接数异常
-
核心指标:
TCP连接数,特别是TIME_WAIT状态连接非常多。- 可能出现
Cannot assign requested address错误。
-
详细问题分析:
- 问题A:TCP短连接
- 描述:HTTP客户端没有使用连接池,每个请求都创建新的TCP连接,用完即关。关闭的连接会进入
TIME_WAIT状态(持续2MSL,通常为1-4分钟),大量此类连接会耗尽客户端端口。 - 根因:编程模型或配置错误。
- 描述:HTTP客户端没有使用连接池,每个请求都创建新的TCP连接,用完即关。关闭的连接会进入
- 问题B:连接池配置不当
- 描述:连接池的最大连接数设置过小,导致请求在池外排队,可能引发超时,并可能诱发客户端创建更多短连接。
- 问题A:TCP短连接
-
排查命令:
ss -s(查看总结),netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'(查看各状态连接数)
3. 现象:TCP 重传率高 / 丢包严重
-
核心指标:
retrans(重传报文数)高。retrans/s(每秒重传数)高。
-
详细问题分析:
- 问题:网络不稳定、拥塞。
- 影响:重传会导致请求延迟(RTT)大幅增加。
- 排查命令:
netstat -s(查看TCP统计信息)
五、最隐蔽的情况:所有资源都“空闲”,但性能不达标
-
现象:
- CPU、内存、磁盘I/O、网络带宽使用率均不高。
- 系统负载(Load Average)也正常。
- 但压测QPS就是上不去,响应时间(RT)却很高。
-
详细问题分析:
-
问题A:外部依赖瓶颈
- 描述:你的应用线程在等待下游服务(如数据库、Redis、第三方API)的响应。此时你的应用线程处于阻塞等待状态,不消耗CPU,看起来“很闲”。
- 根因:下游服务处理能力不足、慢查询、或网络延迟高。
- 排查方向:
- 链路追踪:使用SkyWalking、Zipkin等工具,分析请求时间到底消耗在哪个下游服务上。
- 监控下游:直接监控数据库的CPU、慢查询日志;监控Redis的响应时间。
-
问题B:应用内部资源池瓶颈
- 描述:应用配置的数据库连接池、HTTP客户端连接池的线程数/连接数已满。新的请求需要排队等待池中有资源释放。
- 根因:资源池大小配置过小,或池中连接被长时间占用(这又可能指向下游服务慢)。
- 排查方向:查看应用资源池的监控指标(如HikariCP、Druid的监控界面),看活跃连接数、等待线程数。
-
问题C:压测客户端成为瓶颈
- 描述:你用来发压的机器(压测机)本身性能不足(CPU、网络),无法产生足够的压力。
- 排查方向:监控压测机本身的资源使用情况。解决方案是使用分布式压测,或者用更高配置的压测机。
-
总结与实战排查流程
- 全局观览:使用
top,htop,vmstat 1快速查看四大核心资源(CPU, Memory, IO, System)的总体情况,找到最明显的瓶颈点。 - 深入剖析:
- CPU高 -> 使用
profiler工具分析代码热点。 - 负载高但CPU低 -> 使用
iostat,sar -n DEV 1检查I/O。 - 内存增长 -> 使用内存分析工具找泄漏点。
- 所有资源都低 -> 检查下游依赖和内部资源池。
- CPU高 -> 使用
- 关联分析:结合应用日志(特别是错误日志、超时日志)和链路追踪数据,将系统指标与业务逻辑错误联系起来,形成完整的证据链。
通过这种由浅入深、由表及里的分析,你可以精准地定位压测中暴露出的性能瓶颈,并进行有效的优化。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120527

浙公网安备 33010602011771号