十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JMeter压测实战:从施压到定位系统瓶颈的完整指南

JMeter压测实战:从施压到定位系统瓶颈的完整指南 搞性能压测这活儿干得多了就会发现一个扎心的现实大部分人跑完 JMeter 只是拿到了一张“接口平均响应时间 800ms”的报告但系统真正的瓶颈在哪儿还是一头雾水。我自己带过不少测试同学做压测也去帮别人排查过线上故障。最典型的一个场景是压测时 TPS 死活上不去但 CPU 和内存看起来都没跑满数据库连接数也没爆那问题到底出在哪儿这时候如果你只会用 JMeter 默认的聚合报告看几个数值那基本等于白测了。JMeter 本身只是一个“施压工具”它的职责是制造流量、模拟真实用户请求告诉你“系统在多少并发下开始变慢”。但瓶颈定位这事儿是 JMeter 和监控工具、排查手段结合之后才能完成的活儿。这篇文章我想从一个实战角度把“用 JMeter 压测找到软件瓶颈”这件事拆开揉碎了讲清楚。这里我要展开讲的不只是“怎么点按钮跑一个压测”而是一套从准备、执行、定位到验证的完整思路。你可能是刚入门的测试新人也可能是被领导临时抓来扛压测任务的开发同学这篇文章的内容应该都能帮上忙。1. 压测前的准备工作先搞明白你在为谁压、压什么很多人的压测是这么开始的打开 JMeter新建线程组填个 100 并发跑起来然后盯着聚合报告看。这个流程看似没毛病但实际上有个致命缺陷——你没想清楚要测什么也没确认测试环境是干净的。1.1 明确压测目标和业务场景压测不是“把并发数调大然后看系统崩不崩”。在动手之前至少要回答这几个问题这次压测是为了验证新上线的功能能不能扛住预期的流量还是为了排查线上已经暴露的性能问题核心业务链路是什么下单、支付、登录这类写操作和商品列表、详情这类读操作压测策略完全是两回事。预期的线上峰值 QPS/TPS 是多少平时流量的多少倍算是“超预期”这决定了你要跑多大的压力。我举个实际例子。之前帮一个电商小程序做上线前验证业务方说“预计上线首日会有 5 万用户访问”。这个数字听着挺大但拆开算一下5 万用户分布在一天 24 小时里真正的高峰可能在晚上 8 点到 10 点两个小时内的请求量可能占全天 60% 以上。粗算下来高峰每秒的请求量也就是几十到一两百。那压测的目标就很清楚了验证系统在 200 QPS 下能否稳定运行以及 500 QPS 下什么时候开始报错变形。目标明确了后面所有的线程组配置、监控指标、通过标准才有的放矢。1.2 环境检查测试环境不等于生产环境这一点我踩过太多次坑了。测试环境的机器配置比生产低、数据库里数据量只有几万条压出来的结果参考价值很有限。更麻烦的是很多公司测试环境和生产环境走的是同一套数据库或者同一组缓存你一压测线上业务直接跟着遭殃。所以压测前的环境检查一定要做这几件事确认压测环境是独立部署的至少应用服务、数据库、缓存不能和生产混用。数据库里的数据量要接近生产规模。举个最简单的例子一张订单表生产有 1000 万数据测试环境只有 100 条那查询走不走索引、有没有慢 SQL压测结果是完全不一样的。确认压测机本身的性能足够。Jmeter 是 Java 应用单机压测时如果线程数开太高Jmeter 自身会成为瓶颈压出来的结果就不准了。一般建议 Jmeter 所在机器的 CPU 占用不要超过 70%否则就得考虑分布式压测了。1.3 监控体系先行没有监控数据的压测等于盲人摸象这是我想重点强调的一点。压测过程中如果不能实时看到应用服务器的 CPU、内存、磁盘 IO、网络带宽、GC 情况那压完之后你只知道“系统慢了”但完全不知道“为什么慢”。JMeter 本身能提供的监控能力其实有限顶多是通过一些插件看服务器的性能指标。我自己常用的组合是监控对象推荐工具核心指标应用服务器nmon / atop / dstatCPU 使用率、Load Average、内存、磁盘 IOJVM 运行状态jstat / VisualVM / ArthasGC 频率、堆内存使用、线程数数据库慢查询日志 / Performance Schema慢 SQL、连接数、锁等待中间件Redis、MQ 等info 命令 / 自带监控面板命中率、阻塞客户端数、堆积消费数全链路监控SkyWalking / Zipkin / CAT各服务调用耗时、错误率压测的时候一定要开着一个实时监控终端一边压一边观察指标的变化趋势。千万不要等压测跑完了再去看因为压测结束之后很多指标会迅速回落你就啥也看不到了。2. JMeter 压测执行中的关键设置这些参数错了结果会骗你环境准备好、目标定清楚之后才轮到 JMeter 的上手配置。这里面有几个最容易被忽略但又直接影响结论的关键点。2.1 线程组的选择到底用哪种线程组关于线程组网上争论很多。我用下来最务实的分类方式是Thread Group普通线程组适合大部分常规压测场景。线程数就是并发用户数Ramp-Up Period 控制启动时间。如果你只是想知道系统在固定并发下的表现这个就够用了。Ultimate Thread Group最终线程组更适合做阶梯加压。比如你想观察系统从 50 并发到 200 并发过程中 TPS 的变化曲线看看在哪个点开始出现拐点用这个就能很直观地压出一条趋势线。Concurrency Thread Group并发线程组适合配合 Throughput Shaping Timer 使用可以精确控制压测过程中的吞吐量常用于全链路压测、容量规划这类精细场景。对于“找瓶颈”这个目标我的建议是优先用阶梯加压的方式而不是一上来就直接开 1000 线程。因为你得先知道系统在多少并发时开始出现性能拐点。比如先从 50 并发开始每 30 秒加 50一直加到 500同时观察 TPS 和响应时间的变化。如果 TPS 在 200 并发时就不再上涨、甚至开始下跌那这个点附近就是你需要深挖的瓶颈区域。2.2 参数化永远不要用死数据跑压测新手最容易犯的一个错误是登录接口写死一个账号查询接口写死一个 ID然后所有线程都在打同一个数据。这样压测的结果基本是失真的原因有两个热点数据会掩盖真实的性能问题。比如你查的是同一条订单这条数据可能早就被数据库缓存在内存里了响应时间自然快得离谱但这不代表系统的真实处理能力。数据冲突会导致压测失败。比如你用同一个账号并发下单大概率会触发幂等校验或者库存扣减失败接口直接报错了。正确做法是用 JMeter 的 CSV Data Set Config 做参数化准备一批真实分布的数据。比如用户 ID、商品 ID、订单号各准备几千条压测时让每个线程取不同的值去请求。# users.csv 文件内容示例 user_id,order_id,token 10001,20001,abc123 10002,20002,def456 10003,20003,ghi789在 JMeter 里添加 CSV Data Set Config设置好文件名、变量名、分隔符然后在请求里用${user_id}这种方式引用就可以。这里有个细节值得注意CSV 数据文件里不建议表头设置不当否则第一行数据会被跳过。另外如果 CSV 文件里的数据数量少于线程数设置了“Recycle on EOF True”会导致循环使用同一批数据这在长时间压测时是合理的但短时间高峰压测时要注意数据可能重复。2.3 监听器的正确用法什么时候该加什么很多教程会教你把“View Results Tree查看结果树”开着然后看每个请求的响应详情。但实际压测时我强烈建议不要开结果树因为结果树会把每个请求的响应内容保存下来高并发下这个操作本身就占用大量内存和磁盘 IO直接影响压测的准确性。正确的做法是压测过程中只看Summary Report汇总报告或者Aggregate Report聚合报告的实时数据。用Backend Listener配合 InfluxDB Grafana 做实时监控大屏这个组合可以非常直观地看到 TPS、响应时间、错误率的变化趋势比 JMeter 自带的监听器好用太多。压测结束后再用结果树去抽样检查用 Debug Sampler 确认参数化取值是不是对的。这里也顺带说一句不要过度依赖聚合报告里的“Average”这个指标。平均值在性能分析里是最容易骗人的——假设有 1000 个请求其中 999 个都是 100ms但有 1 个请求卡了 15 秒平均值就是 114ms看起来“还可以”但实际上那 1 个卡顿的请求可能就是系统即将崩溃的信号。看曲线、看分位数TP95/TP99才有意义。2.4 断言和超时设置别让错误请求污染了结果我见过不少同学的压测报告里 TPS 高得吓人仔细一看才发现大部分请求都报错了。为了避免这种情况接口请求里要加断言最常用的是Response Assertion响应断言判断响应文本中是否包含某个特征字符串。比如请求成功时接口会返回status: 0那就断言这个内容。超时设置同样重要。HTTP 请求默认的响应超时可能是 0也就是无限等待。如果接口卡住了这个线程会一直占着不释放导致 JMeter 端的有效并发数越来越低压测结果完全失真。我给的建议是连接超时设为 3000ms响应超时设为 6000ms可以根据接口的真实耗时来调整但不能不设。3. 瓶颈定位的核心链路从应用日志到数据库的层层排查压测跑完了报告也出来了TPS 在某个并发点开始下降响应时间开始飙升这时候才进入整个流程中最关键的一环——定位瓶颈在哪一层。我习惯按照下面这个链路一层一层往下查每次都能很快缩小问题范围。3.1 第一步看应用服务器的 CPU 和 Load 情况压测过程中如果发现 TPS 上不去第一个要看的是应用服务器的 CPU 使用率。如果 CPU 已经跑满接近 100% 或者 Load Average 远高于 CPU 核数那说明系统的计算资源已经打满了。这时候用top -H -p pid查看进程内线程的 CPU 占用定位到具体线程再用jstack把线程堆栈打出来就能看到这个线程在跑什么代码。如果 CPU 才用了 30%但 TPS 已经到天花板了那就要往下面查了——瓶颈很可能不在应用服务器本身而在下游依赖。还有一种很常见的现象是 CPU 使用率忽高忽低像锯齿一样波动。遇到这种情况优先排查是不是有定时任务在压测期间触发比如每天整点的数据清理任务、缓存刷新任务。之前我在一次压测中就碰到过 TPS 周期性掉零的情况最后定位到是应用里一个定时任务每 5 分钟做一次全量缓存重建每次重建期间接口响应就会暴涨。3.2 第二步排查 GC 和 JVM 内存Java 应用压测时 GC 问题非常常见。在高并发下如果新生代空间设置得不够对象频繁进入老年代就会触发频繁的 Full GC每次 Full GC 都会 Stop The World表现为响应时间偶尔出现极大的尖刺。在压测启动前用jstat -gcutil pid 1000实时观察 GC 情况如果 Full GC 次数在压测过程中快速增加那基本上可以断定是内存分配压力过大或者存在内存泄漏。如果 Young GC 那么频繁说明新生代设置太小或者代码里大量创建了生命周期短暂的对象。解决方向通常是调 JVM 参数比如把堆内存调大、设置合理的-Xms和-Xmx相等或者是用 G1 垃圾回收器替换 CMS。但我要提醒一句调参只能缓解真正的问题往往出在代码里。比如某些接口高频调用时创建了大量的中间对象或者日志框架的异步队列没有设置上限。这些要结合堆 dump 分析才能挖出来。3.3 第三步查数据库和慢 SQL应用服务器 CPU 不高、GC 也正常但 TPS 就是上不去——这一步基本要把问题锁定在数据库或者外部依赖上。数据库排查有个非常高效的起点打开慢查询日志看压测期间有哪些 SQL 执行时间变长了。很多性能瓶颈的根源就是一条 SQL 没走索引或者走了索引但数据量太大扫了太多行。我之前定位过一个真实的线上问题一个列表查询接口平时响应 50ms压测期间直接飙到 5 秒。查慢查询日志发现这条 SQL 在压测时扫描了 800 万行数据。原因很简单测试环境的数据量只有 2 万条索引一直假装有效但生产环境的数据量一大之前建的联合索引因为字段顺序设计不合理根本无法命中。这就是我在前面强调“压测环境数据规模要接近生产”的根本原因。数据库层面的排查还需要关注几个点数据库连接池是否被打满。连接池满意味着应用线程在等待获取连接表现就是响应时间飙升但 CPU 不高。可以通过 Druid 或者 HikariCP 的监控面板看活跃连接数。锁等待。压测时如果出现大量更新操作很容易出现行锁冲突比如并发更新同一条订单数据。数据库的show processlist能看到大量Waiting for lock的会话。缓存命中率。Redis 的命中率如果突然下降大量请求穿透到数据库数据库压力瞬间上来也会导致整体性能下降。3.4 第四步别忘了外网带宽和连接数限制这一层容易被忽略因为你用 JMeter 压测时请求是走网线的。如果压测机和服务器之间有防火墙、负载均衡器Nginx/LVS这些中间设备本身也会成为瓶颈。最常见的问题是Nginx 的最大连接数或者 worker 进程数配置不当。默认配置下 Nginx 单 worker 的连接数是有上限的高并发时 Nginx 直接拒绝连接表现为压测端出现大量 Connection Refused 错误。处理方法也很直接调大worker_connections同时确认worker_processes是不是和 CPU 核数匹配。另外如果压测机的带宽不够也会导致 TPS 上不去。比如压测机通过公网访问服务器而公网带宽只有 5Mbps压测时发出去的请求和接收的响应一旦超过带宽上限网络就会成为瓶颈。判断方法也简单压测时在压测机上用iftop看实时流量如果流量打满那就说明压测机自身才是瓶颈这种情况就要考虑在内网执行压测或者用分布式压测。4. 压测报告解读指标背后藏着的真相跑完压测、定位到瓶颈之后还有一个同样重要的环节——把结果讲清楚。一份合格的压测报告不是把 JMeter 聚合报告截图贴上去就完事了而是需要结合指标趋势给出一套完整的解读。4.1 核心指标不是数字而是曲线我在写报告时最在意的三个指标是TPS 曲线、响应时间分位数曲线、错误率曲线。指标解读方法TPS随着并发增加TPS 线性增长说明系统还有余量TPS 增长放缓然后持平说明到了容量上限TPS 掉头向下说明系统已经过载开始崩溃TP99/TP95如果平均响应时间还行但 TP99 突然飙升说明有一部分请求被卡住了可能存在长尾请求或者某类突发慢请求错误率正常压测在系统容量范围内错误率应该是 0一旦出现错误率上升说明系统已经进入过载状态举个例子说假设压测结果表格长这样并发数TPS平均响应时间(ms)TP99(ms)错误率504801122650%1007801343100%2009002418920%30092035823000.2%50086065558005%这份数据可以读出几个重要结论200 并发时 TPS 出现明显减速带300 并发时基本到顶500 并发时已经开始崩了。那么可以说系统的容量拐点在 200 到 300 并发之间最优运行区间是 100 并发左右。4.2 常见的“伪瓶颈”案例分析对于压测结果经常会出现一些“假象”如果不深入分析很容易被带偏。第一个是Jmeter 自身成为瓶颈。如果你发现压到 1000 并发时Jmeter 的 CPU 已经 100% 了但服务器端负载很低这时候测出来的 TPS 其实是 Jmeter 的极限不是系统的极限。判断方法是看服务器侧的实际接收请求数或者把 Jmeter 换成分布式压测看看 TPS 能不能提上去。第二个是线程数≠并发数。HTTP 请求是请求-响应模式的如果响应时间 100ms线程数 100那理论上 TPS 最高能到 1000但如果响应时间是 1 秒同样的 100 线程TPS 只能到 100。很多同学只调线程数不看响应时间发现 TPS 不涨就以为系统瓶颈了其实只是线程数不够而已。第三个是响应时间上涨但 TPS 不变。这说明系统进入了一种“排队”状态——并发上来了系统的处理能力没变请求在后面排队等表现就是 TPS 持平但响应时间越来越大。这种情况说明系统的某个资源通常是数据库连接池、线程池已经满了新增的请求在池子外面排队。4.3 一份合格压测报告应该包含什么以我个人的经验一份能让人看懂的压测报告至少应该包含压测基本信息测试时间、压测环境、压测机配置、网络拓扑、压测工具和版本。压测场景设计业务链路、并发策略阶梯加压还是固定并发、压测时长、参数化方式。核心指标数据TPS、响应时间平均/TP95/TP99、错误率最好带上并发梯度下的趋势图。监控数据服务器 CPU、内存、Load、GC、数据库连接数、慢 SQL 数量等。结论与建议系统可承载的容量是多少最优并发区间是多少瓶颈点在哪里给出具体的优化方向。优化措施验证如果是优化后复测要带上优化前后的对比数据和优化说明。5. 几条实战经验总结少走弯路的压测心法最后分享几个我做了无数轮压测之后沉淀下来的经验算不上什么高深理论但每一条都来自实战希望能帮你少踩些坑。5.1 先跑短压测确认链路通再跑长压测找隐患很多人一上来就直接设置 10 分钟、30 分钟的压测时长跑完才发现 CSV 参数化文件路径不对、断言写错了、请求头少了 token 字段白白浪费了时间。我的习惯是先用 1 分钟跑一个低并发比如 10 线程的冒烟压测确认请求全部成功、响应时间正常然后再把并发和时长调到目标值。这样能快速暴露脚本本身的问题而不是让脚本问题和技术问题混在一起干扰分析。5.2 压测一定要做监控快照而不是事后回忆在压测过程中每调整一次并发档位就手动记录一下当前服务器各项指标的快照。比如在 50 并发时记录一次 CPU、Load、GC、数据库连接数在 100 并发时再记录一次。这样在分析时就能把“当时的并发数”和“当时的系统状态”对应起来而不是压测结束后拿着一个平均数据瞎猜。如果条件允许用 InfluxDB Grafana 自动记录是所有方案里最省力的。5.3 每次只改动一个变量压测定位瓶颈最忌讳“一次改多个东西”。比如发现响应时间慢你同时改了 JVM 参数、调了数据库连接池、还优化了一条 SQL最后响应时间确实变快了但你根本不知道是哪个改动起的作用。正确的做法是每次只调整一个变量重新压测对比数据再决定下一步。这样排查效率反而更高也更容易定位到真正的问题根因。5.4 瓶颈定位是分析的过程不是压测的结果我做过的所有压测项目里几乎没有一次是“压完顺手就找到瓶颈”的。每一次定位到一个瓶颈背后都有一段“数据异常 → 猜想 → 验证 → 确认”的链路。比如先发现 TP99 飙高然后看到 GC 频率异常接着通过堆 dump 发现某个对象被疯狂创建最后定位到代码里某个循环中 new 了大量对象。这个过程需要耐心也要求你对系统有足够的理解。如果能把这些基本功练好JMeter 在你手里就不只是一个“发起压力的工具”而是一套完整的性能问题排查方法论中的入口。以后再遇到“系统卡了”“接口变慢了”“上线前想验证下能不能扛住”你就不会慌因为你已经知道该怎么一步步把瓶颈揪出来了。
返回列表