先想把这篇的手感定下来。这几年性能测试大家聊得最多的话题,从怎么装JMeter、怎么写脚本,慢慢转移到“到底该在什么环境里压”。绝大多数团队在测试环境压出来的结果,到了生产环境完全对不上,然后开始互相甩锅:开发说测试环境配置不对,测试说生产代码版本有问题,运维说你们压测把线上搞慢了。我在这个行业前后待了十年出头,压测环境这块踩的坑比脚本本身多得多。
这篇就拿“搭建线上的性能测试环境参考逻辑图”这个标题展开,把整个环境搭建的树干和枝叶都梳理一遍。不管你用的是JMeter还是LoadRunner,不管你的系统是单体还是微服务,这套逻辑基本都能套。我会按整体设计思路、环境拓扑、工具选型、监控体系、数据准备、指标分析、问题排查这么一条线来讲,中间穿插一些我实际踩过的坑和验证过的参数,争取让看完的人能直接拿去用。
1. 搭建性能测试环境前,先想清楚这几个根本问题
1.1 为什么不能直接在测试环境压,也不能直接上生产压
先说结论:性能测试环境的价值,在于它是生产环境的一个“足够接近的替身”。很多团队问的第一个问题是,能不能直接在测试环境压?我的回答通常是:压出来的结果你自己信吗?测试环境最大的问题是配置缩水。生产是8核16G的例子,测试环境给个2核4G,数据库连接池减半,缓存集群从3个节点缩到1个,这种环境压出来的TPS和响应时间,没有任何参考价值。更麻烦的是,测试环境的数据量通常只有几十万条,索引效果、SQL执行计划跟生产完全不一样,你压出来的瓶颈可能是测试环境特有的假瓶颈。
那能不能直接上生产压?这个问题得分情况。大厂确实有全链路压测,但人家有完整的压测平台、流量隔离、链路染色、熔断降级预案,还有专门的压测团队和值班体系。普通团队直接在生产压,一旦把数据库连接池打满,或者把某个核心接口的线程池耗尽,业务直接就挂了,第二天你和运维都被请去喝茶。所以对绝大多数团队来说,最务实的路径是:搭一套独立的、规格和生产对齐的压测环境,在这套环境里做绝大多数验证。这套环境用完可以降配或者释放,成本可控。
1.2 逻辑图的核心:一个原则、两条链路、三套数据
标题里说的“参考逻辑图”,在我理解里不是画一张拓扑图那么简单,而是要把环境搭建的逻辑链条理清楚。我自己做这套东西的时候,心里始终装着一个原则、两条链路、三套数据。
一个原则,是环境等价性。你搭的环境,必须在硬件规格、软件版本、配置参数、数据规模、网络链路这五个维度上尽量贴近生产。任意一个维度差了,结果就可能谬以千里。两条链路,一条是业务链路,一条是监控链路。业务链路指你的压测请求从压测机到网关、应用、中间件、数据库的完整路径;监控链路指从操作系统到应用、到中间件、到数据库的指标采集路径。很多团队业务链路搭得挺好,监控链路没跟上,最后压测完了只能报出TPS和响应时间,问CPU多少、GC什么样、数据库慢查询有几条,全答不上来,等于白压。三套数据,是基础数据、铺底数据和压测数据。基础数据指配置项、字典表这类静态数据,铺底数据指业务运行积累的历史数据,压测数据指压测过程中实时产生的数据。三套数据的规模、分布和处理方式,我都会在后文详细讲。
1.3 环境隔离方案怎么选:独立物理环境、公有云隔离,还是容器化
环境搭建的第四个根本问题是“搭在哪”。我见过三种主流方案,各有适用场景。
第一种是独立的物理环境。机房里有几台服务器,专门用来做压测。优点是性能稳定,没有邻居干扰,适合对性能要求极高、且吞吐量很大的系统。缺点是成本高,机器利用率低,大部分时间这些机器是闲置的。
第二种是公有云上的隔离环境。在云上划一个独立的VPC,按生产的规格购买一批云主机,压测完直接释放。这种方案我现在最常用。原因很简单:第一,成本可控,按量付费,压测一天的成本可能就几百块;第二,规格选择灵活,生产是16C32G,我就买同规格的,很方便对齐;第三,带宽、安全组、负载均衡这些网络组件都可以模拟出来。需要提醒的是,云主机的性能稳定性不如物理机,尤其是突发性能,压测前最好先跑一轮CPU和内存的基准测试确认一下。
第三种是容器化方案。用K8s部署一套临时环境,通过资源Limit控制容器规格。这种方案胜在启动快、环境一致性高,适合微服务架构、且对硬件性能差异不敏感的系统。但容器在CPU、内存、网络IO上有一定的性能损耗,如果你的系统对性能精度要求很高,建议至少用裸金属节点来跑关键组件。
2. 环境拓扑长什么样:从压测机到数据库的每一层
2.1 一套标准压测环境的层级划分
讲完了思路,我们落到具体的架构图上。一套标准的线上性能测试环境,从上往下分六层:压测负载层、接入层、应用层、中间件层、数据层、监控层。
- 压测负载层:JMeter或LoadRunner的控制器和压力机,负责产生并发流量。
- 接入层:Nginx、网关或云上的负载均衡服务,负责把流量分发到下游。
- 应用层:你的业务服务,可能是单体应用,也可能是十几个微服务。
- 中间件层:缓存(Redis)、消息队列(Kafka、RocketMQ)等。
- 数据层:MySQL、PostgreSQL、MongoDB等数据库。
- 监控层:独立于业务链路之外的一套采集和展示系统,这里要特别注意,监控组件千万不要装在压测环境里复用,我后面会解释为什么。
这里的每一层,都应该和你生产的对应层在规格上对齐。你不需要把整个生产环境原样搬过来,但至少要覆盖被测业务链路涉及的全部组件。
2.2 每层资源规格怎么定:一个可参考配置清单
很多新手拿到这个逻辑图会问,每层到底该配多大的机器?这里我给一份参考配置,基于一个中等规模的业务系统:日活用户10万左右,核心接口平时TPS在500左右,峰值2000上下,数据库高峰期QPS在5000左右。如果你手里的系统规模不同,按比例调整即可。
| 层级 | 组件 | 推荐规格 | 说明 |
|---|---|---|---|
| 压测负载层 | JMeter 压力机 | 8C16G × 2~N台 | 单台JMeter建议并发不超1000线程,超出后横向加机器 |
| 接入层 | Nginx | 4C8G × 2 | 建议和生产同版本同配置 |
| 应用层 | 业务服务 | 16C32G × 3~5 | 规格与生产对齐,JVM参数保持一致 |
| 中间件层 | Redis | 8C16G × 3(集群) | 和生产保持一致的分片数 |
| 中间件层 | Kafka | 8C16G × 3 | 副本数保持和生产一致 |
| 数据层 | MySQL | 16C64G × 2(主从) | 生产必须是主从的,压测环境也必须是 |
| 监控层 | Prometheus + Grafana | 4C8G × 1 | 独立部署,不要和被压服务混部 |
这里有一个原则需要反复强调:压测环境的所有瓶颈,都应该出现在被测系统上,而不是出现在压测工具本身。如果你压了半天,最后发现JMeter机器CPU已经100%,而应用服务器CPU才40%,那这次压测就是无效的。所以在正式压测之前,一定要先把压力机的能力对搭出来,确认每台压力机能稳定发出足够的流量。
2.3 网络拓扑里的隐藏坑:时延、带宽和链路复用
网络是一个很容易被忽略、但影响巨大的环节。本地环境压测,压力机和应用服务器之间延迟通常小于1毫秒;但线上环境,哪怕是在同一个机房,真实的服务间调用延迟也会有0.5到2毫秒。这意味着你在本地压测环境里跑出来的响应时间,拿到线上环境可能直接翻几倍。
我的建议是,压测环境的网络拓扑也要尽量模拟生产。如果生产环境里客户端的请求是先经过DNS解析、再经过一层负载均衡,那压测环境也该这么搭。更细节的一点是带宽。曾经有一次我帮一个团队压测,压到2000并发的时候发现TPS上不去了,查了半天,最后发现是压力机所在网段的带宽被打满了,出网流量到顶了。从此之后,我在搭环境清单里加了一条:确认压力机出口带宽不低于被测系统峰值流量所需带宽的1.5倍。
还有一点要提醒的是,关掉不必要的防火墙和安全组策略。不是说让你忽略安全,而是说有些安全组件如果在压测流量路径上,会产生额外的计算开销,比如WAF的规则过滤、防火墙的会话追踪,这些在真实生产环境里确实存在,但你压测的重点如果是应用本身的性能,这些组件的干扰会掩盖真实瓶颈。如果你要测的是整条链路(包括安全组件),那就另说,但一定要想清楚自己到底要测什么。
3. 工具选型:JMeter和LoadRunner怎么选,分布式压测怎么做
3.1 JMeter和LoadRunner的对比:别再纠结,按需选择
工具选型是每个做性能测试的人都会遇到的问题。JMeter和LoadRunner的争论持续了十几年,我的态度很明确:看团队情况和个人习惯,两个都能完成90%以上场景的压测需求。为避免大家挑花眼,我把核心差异整理成一个表格:
| 对比项 | JMeter | LoadRunner |
|---|---|---|
| 价格 | 开源免费 | 商业授权,费用高 |
| 脚本开发 | Java/Sampler,上手快,但复杂场景需写代码 | 支持C/Vugen等多种协议脚本,VUGen录制功能强 |
| 协议支持 | HTTP/HTTPS、JDBC、JMS、WebService等,很全 | 支持的协议更广,包括一些老的客户端/服务器协议 |
| 分布式压测 | 原生支持,通过Agent方式扩展压力机 | 自带负载生成器,控制台集中管理 |
| 性能分析能力 | 需要搭配第三方监控工具 | 自带Analysis分析套件 |
| 学习成本 | 较低,文档和海量教程 | 较高,但GUI操作在入门阶段更直观 |
| 适合场景 | 互联网团队、微服务、API接口压测 | 企业级复杂协议、金融传统系统 |
我的实践经验是,如果是互联网公司做Web、API层面的压测,JMeter是性价比最高的选择,免费、社区活跃、遇到问题搜一下就有人踩过。如果是金融公司,系统用的是某种老协议,比如Tuxedo、CICS这类,那LoadRunner几乎是没有替代的。所以工具本身没有绝对的好坏,先看你的被测系统用什么协议,再决定工具。另外说一句,JMeter千万别用GUI模式去跑正式压测,GUI模式本身会占用大量内存和CPU,结果不准。要压测就进入命令行模式:jmeter -n -t test.jmx -l result.jtl -e -o report,这样跑出来的数据才是真实可靠的。
3.2 JMeter脚本中影响结果准确性的三个关键设置
如果你选了JMeter,有三个设置直接决定结果是“参考值”还是“真实值”。
第一个是线程组设计。不要一味地加大并发线程数。线程数只是“并发用户数”的本义,它由业务模型决定:如果你的系统在线用户1万,平均并发只有500,那你压测的目标并发应该锚定在500而不是1万。线程数的设置,要配合Ramp-Up时间。我常用的做法是,总并发500的压测,Ramp-Up时间设置为60秒,这样每秒新增8到9个线程,模拟真实用户逐渐进入系统的过程。如果你瞬间把500个线程全部怼上去,那测出来的是系统的尖峰冲击能力,不是稳态性能。
第二个是参数化数据。每个线程的请求参数必须不同,否则你会压出一个“缓存友好型”的假结果,尤其容易命中Redis缓存或数据库的查询缓存。JMete里常用CSV Data Set Config来参数化,把准备好的测试数据放在一个文件里,让每个线程读取不同的数据。这里有两个细节:文件里数据量一定要大于线程数乘以循环次数,否则循环回绕的时候数据会重复;二是在“CSV Data Set Config”里选好Sharing mode,默认是All threads,如果文件大,可以用Current thread,每线程独享一段数据,避免线程争抢IO文件句柄导致压力机性能下降。
第三个是断言和监听器。很多人建脚本会顺手加一个“View Results Tree”,压测的时候发现TPS总上不去。原因很简单,这个监听器会把每个响应都存下来,在高并发下CPU和内存都被吃光了。压测时不要挂这种高开销的监听器,只看聚合报告Summary Report就够了。如果你想做接口正确性校验,用一个“Response Assertion”去断言状态码和关键字段就行,不要在压测过程中开树形监听器。
3.3 分布式压测的搭建要点
单台JMeter的并发能力有限,通常到1000到2000线程就会出现瓶颈。这时候需要做分布式压测。JMeter的分布式压测结构是:一个Master节点(控制机)+ 多个Worker节点(压力机),Master负责下发脚本、汇总结果,Worker负责实际发送请求。
搭建时有几个点必须注意。Master和所有Worker的JMeter版本必须完全一致,主版本不一致会导致Agent无法连接,我见过太多人栽在这里。所有节点的JDK版本也要统一,最好是同一个小版本。启动顺序是先启动所有Worker(jmeter-server),再启动Master执行脚本。监听器的问题在分布式模式下更容易放大,Master如果在压测过程中实时聚合结果,很容易成为新的瓶颈,建议把各Worker的结果先写到本地文件,压测结束后再合并。
关于压力机数量,我一般按“一台压力机支撑500并发线程”的粗颗粒度来规划,也就是说2000并发需要4台压力机。还要提醒的是,压力机之间、压力机到被测系统之间的网络延迟要尽量一致,如果机器分布在不同的机房或不同的机架上,延迟不一样,最终统计出来的平均响应时间会失真。
4. 监控体系:压测中最重要的“眼睛”,也是最容易被忽视的环节
4.1 监控指标分层梳理:从OS到业务一个都不能少
很多团队的压测报告只有两张图:TPS曲线和响应时间曲线。流量稍微一大就发现响应时间变长了,但瓶颈在哪完全不知道。真正的性能测试,背后必须有一套完整的分层监控体系,核心是按OS指标、应用JVM指标、中间件指标、数据库指标、业务指标这五个维度来采集。
| 监控对象 | 关键指标 | 参考阈值 |
|---|---|---|
| 操作系统 | CPU使用率、负载、内存、磁盘IO、网络带宽 | CPU < 70%,load < CPU核数 |
| JVM | GC频率、GC耗时、堆内存使用率、线程数 | Full GC < 1次/分钟 |
| Web容器 | 活跃线程数、队列长度、连接数 | 活跃线程数 < 最大线程数的70% |
| Redis | 命中率、内存使用、连接数、慢查询 | 命中率 > 90% |
| Kafka | 消息堆积量、消费延迟 | 堆积量稳定或下降 |
| MySQL | QPS、TPS、慢查询数、连接数、InnoDB行锁等待 | 慢查询 < 1%,连接数 < 80%上限 |
| 业务指标 | 核心接口TPS、响应时间P99、错误率 | 错误率 < 0.1% |
这些指标监控的数据要能汇总到同一台Prometheus上,用Grafana做可视化。开多个监控页面来回切是低效的做法,最好做一个压测专用的Dashboard,把五个维度的核心指标放在同一屏。
4.2 监控工具选型:免费开源方案也能做到企业级效果
如果你问我监控方案怎么做,我的答案是Prometheus + Grafana + 各类Exporter,这套组合搞定90%以上的场景。node_exporter采集操作系统指标,jmx_exporter采集JVM指标,redis_exporter采集Redis指标,mysqld_exporter采集MySQL指标,Kafka有专门的kafka_exporter。每类中间件都有成熟的Exporter,不需要自己写。
刚开始搭这套监控的小朋友经常会犯一个错误:把Prometheus和Grafana装在被压测的机器上。这样做最直接的后果是:压测一开始,监控自身的采集进程也在消耗CPU和内存,相当于你的被测环境还被监控拖着走,数据自然不准。监控组件必须独立部署,最好在独立的机器或者独立的容器里。
Grafana的告警功能也一定要配起来。压测过程中我不可能一直盯着屏幕,我会预设几个核心告警:应用服务器CPU超过90%、Full GC发生、错误率超过0.5%、队列堆积量持续上升。一旦触发,告警通知直接发到钉钉或者企业微信。这样压测过程中可以离开工位,出了问题再回来处理。
4.3 监控和压测数据的交叉分析:判断瓶颈点的核心方法
监控体系搭好之后,真正值钱的能力是“看数据”。很多人的压测报告连瓶颈结论都不敢下,就是因为只看单点指标。我给一个我自己一直在用的数据分析路径。
第一步,找到拐点。压测从低并发逐步升到高并发,观察TPS曲线。在某个并发点之前,TPS会随并发上升而线性增长;到了某个点之后,TPS增长变缓甚至下降,这个拐点就是系统的性能极限。
第二步,定位瓶颈。拐点出现后,对比同一时刻不同层级的指标。如果应用服务器的CPU已经接近100%,但数据库CPU才30%,说明瓶颈在应用层,可能要扩容应用或者优化代码。如果MySQL的CPU和慢查询同时飙升,说明瓶颈在数据库SQL。如果Redis命中率突然下降,大量请求穿透到了数据库,说明缓存策略需要重新设计。
第三步,验证结论。定位到瓶颈之后,针对性地做一次小范围的调整,再压一次同样的场景,看TPS和响应时间是否如预期变化。我自己的经验是,绝大多数的性能瓶颈都能通过这套“拐点+分层对比+验证”的方法定位出来。不要靠猜,用数据说话。
5. 测试数据准备:压测结果准不准,一半看数据
5.1 为什么数据规模和数据分布那么重要
测试数据是压测环境里最容易被低估的一环。很多人搭完环境,让开发帮忙灌了几百万条数据就开始压,压出来的结果惨不忍睹,还以为是代码问题,结果一看数据库数据分布跟生产完全是两个样。为什么数据会影响性能?核心原因是数据库的执行计划对数据分布极其敏感。一个字段在某张表里的数据量,是否走索引,数据倾斜程度,这些都直接决定SQL的执行路径。你用一个只有100条数据的表压测,MySQL优化器根本不会选择走索引,而是全表扫描,这在生产上是不可能发生的。
我举个例子。某团队测一个订单查询接口,测试环境订单表只有10万条数据,测出来的P99响应时间是200毫秒。上线之后发现生产P99是800毫秒,排查后发现生产订单表有3亿条数据,SQL走了错误的索引。这不是代码问题,是测试数据没构造好。
5.2 如何按生产数据比例构造压测数据
数据量到底要多大?我的经验是,压测环境的数据量至少应该是生产数据量的1/10,如果能做到1/5更好。数据分布也要讲究,要让数据像生产一样有冷热之分。比如生产上大部分订单是近3个月创建的,历史订单占比很小,你的测试数据也应该遵循这个比例。只灌一批最新的数据,跑出的响应时间都会好看;只灌历史数据,又会让结果恶化。最好的方式是从生产导一份脱敏数据到压测环境,这是最贴近真实的方式。现在有很多脱敏工具可以处理手机号、身份证、地址这些敏感字段,脱敏后的数据在结构和分布上与生产完全一致。
另一个需要注意的点是“数据膨胀”。有些接口,比如对账、报表、统计,是扫描全表的,数据量开根号级别的增长都会导致性能的显著变化。这类接口的测试数据,宁可多灌,不能少灌。
5.3 压测数据的造数和清理策略
数据造好了,怎么灌进去也是门学问。直接通过应用的正常写入接口去造数据,速度太慢;直接用SQL脚本批量插入,又绕过了业务逻辑,数据可能不完整。我的建议是分两步:一半用生产脱敏数据导入,一半用脚本批量造数,但造数脚本要参照业务插入逻辑,保证数据字段完整、分布合理。
压测过程中的临时数据,尤其是写操作的压测,会产生大量脏数据,比如重复注册的账号、多出来的订单,这些数据必须在压测结束后清理,否则会污染这套环境,影响下一次压测结果。我的习惯是提前写好清理脚本,压测完跑一遍,把当前环境下所有以“压测标记”命名的数据清掉。还有一个细节,压测环境中如果要用到支付、短信这类外部依赖,一定要先mock掉或者走测试桩子,避免产生真实的外部调用,不仅影响结果,还可能产生费用。
6. 性能测试核心指标解析:这些数字到底怎么算、怎么解读
6.1 一定要搞清楚的五个核心指标
性能测试报告里最常出现的指标,我逐个说清楚它们的含义和计算口径。
第一个是响应时间。响应时间不是一个单一的数字,它是一个分布。常用百分位数来表示:P50、P90、P95、P99。P99的意思是99%的请求响应时间不超过这个值,这个值才是用户体验的关键,平均值经常被异常值拉得毫无参考价值。比如你压测得到平均响应时间300毫秒,但这个均值是被大量极端慢的请求拉高的,P99已经到了2秒,说明系统在长尾请求上存在严重问题。看响应时间,请直接看P95和P99。
第二个是TPS,即每秒事务数。JMeter的聚合报告里称为Throughput,LoadRunner里称为Transactions per Second。TPS直接反映系统处理能力。做容量规划时,TPS是所有扩容决策的基础指标。
第三个是并发用户数和请求并发数,这两个经常混。并发用户数指同一时刻在线的用户数,请求并发数指同一时刻真正发到服务器的请求数。一个用户可能每5秒才发一个请求,1万在线用户对应的请求并发可能只有2000。做压测时要关注的是请求并发数,而不是在线用户数。
第四个是错误率。错误率的容忍度,取决于业务类型。一般的接口要求错误率低于0.1%,支付类要求更严,基本要零错误。压测过程中错误率突然升高,通常意味着某类资源被耗尽,比如数据库连接池满了、线程池拒绝请求、超时时间过短。
第五个是资源利用率,包括CPU、内存、磁盘IO、网络带宽。这里一个重要原则是,不要盯着单一资源看。你压到应用服务器CPU 100%,但数据库只有20%,瓶颈在应用层;反过来应用CPU 20%,数据库CPU 90%,那就是数据库是瓶颈。没有哪项指标能独立定位瓶颈,必须交叉对比。
6.2 怎么用“性能下降曲线”判断系统韧性
除了看单点数值,还要看系统的“性能下降行为”。我经常用一句话总结:性能测试不是看系统能承受多大压力,而是看系统在压力下怎么衰退。
我在环境搭建和后续的压测执行中,会特别关注三个阶段的曲线形态。第一阶段线性区,TPS随并发线性增长,响应时间基本平稳,说明系统还有很多余量。第二阶段饱和区,TPS增速变缓,响应时间开始明显上升,说明某些资源开始触及瓶颈。第三阶段衰退区,TPS反而开始下降,响应时间急剧上升,说明系统已经开始堆积请求、排队恶化,性能彻底崩坏。
系统A可能在300并发时进入饱和区,500并发时进入衰退区;系统B在400并发进入饱和区,一直到800并发都不衰退。虽然峰值TPS差不多,但系统B显然更稳健,有更大的缓冲余地。选型时,如果两个方案在低成本测试下TPS一致,我倾向于选衰退曲线更平缓的那个。这个判断维度在压测报告中很少人写,但非常重要。
7. 压测执行中的常见问题与排查实录
7.1 最典型的五个问题快速定位表
我在实际执行压测的时候,经常遇到下面五种问题,基本覆盖了80%以上场景。这里整理成速查表,压测过程中遇到问题可以直接按表排查。
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 并发升到一定程度,TPS不再增长,CPU只有40% | 可能是锁竞争或IO瓶颈 | 查看线程是否有大量BLOCKED状态;检查磁盘IO吞吐 |
| 响应时间P99很高,但平均响应时间正常 | 有长尾请求或GC停顿 | 看JVM GC日志;查慢SQL;看Redis大Key |
| 错误率突然飙升,伴随连接超时 | 连接池耗尽或下游超时 | 查看数据库连接池使用率;查看日志里的超时时间 |
| 压测机CPU已打满,但TPS还没有达到预期 | 压测机成为瓶颈 | 减少单台并发线程数,增加压力机 |
| 应用内存持续上涨,Full GC频繁 | 可能存在内存泄漏或大对象分配 | dump堆内存分析;查看GC日志中每次GC后的内存回收情况 |
7.2 一个真实案例:加了缓存后TPS反而下降的诡异问题
分享一个真实案例,很有代表性。某业务系统本来直接查数据库,单接口TPS在800左右。为了提升性能,开发加了一层Redis缓存,结果压测时发现,加了缓存之后TPS反而掉到了600。当时开发完全想不通,业务逻辑变简单了怎么性能还倒退了。
排查过程是这样的:先看应用服务器,CPU并没有升高,反而降低了,说明应用计算没有瓶颈。再看Redis,发现命中率非常高,但Redis的CPU使用率已经到95%。问题找到了,瓶颈在Redis本身。进一步看,这个接口的缓存Key存在一个明显的热点问题:所有请求都落在同一个Redis分片上,单个分片的CPU打满,整个集群的性能被这个热点分片拖垮了。解决办法是给缓存Key加上随机后缀做散列,让请求均匀分布到多个分片上。改完之后,TPS回到了1200,比原来直接查库还提升了50%。
这个案例给我的启发是,加缓存并非一定是性能优化,缓存本身也可能成为新的瓶颈。做性能调优和压测验证时,眼光要放大到整条链路上的每个组件,不能只看自己改动的那一段。
7.3 压测收尾的三件套:数据清理、报告归档、环境复查
压测结束之后的收尾工作,做得好的团队和不做的团队,差距会随着时间的推移越来越大。
第一件事,清理压测数据。前面已经提到过,包括标记性数据清理、日志清理、缓存清理。如果不清理,下一次压测环境的数据就是混着上次脏数据的,结果分析会越来越混乱。
第二件事,报告归档。压测报告不能只保留PDF或者Word,还要保留原始数据文件,比如JMeter生成的jtl文件、Grafana导出的监控截图、测试脚本和配置文件版本。我见过太多团队在三个月后想对比版本优化效果时,发现上一轮的原始数据已经找不到了。归档能让每一次压测成为下一次的基线,积累久了,你就是团队里的性能数据库。
第三件事,环境复查。记录一下这套环境还有没有遗留的资源,比如云主机是保留还是释放,端口有没有回收,监控组件还在不在跑。很多团队在压测环境搭建完成后没有做资源管理,半年后发现云平台上挂着一堆没人用的机器,成本白白流失。
8. 一些值得再说的实操心得
写到最后,补充几个我认为对搭建性能测试环境最有帮助的个人经验。
第一,压测环境的版本管理要严格。操作系统版本、JDK版本、中间件版本、应用代码版本,在压测开始前记录清楚,这决定了压测报告对生产的指导价值。有一次我们压测结果比生产好了不少,最后排查发现压测环境的Nginx版本比生产高了两个小版本,性能有优化,但这个版本在生产还没上线,压测结果直接不能用。
第二,压测过程中要有人盯着,但不是一直盯数据,而是关注告警。压测前把告警阈值全部设好,告警来了再处理,重点盯拐点和崩溃点有没有出现。这个习惯能让你同时并行推进多轮压测,效率翻倍。
第三,压测环境不能做到和生产100%一致,不要为此焦虑。我也搭过很多次环境,总会遇到某些规格在生产有、环境配不到的情况。这时候你要做的是记录差异,并在报告里明确标注这些差异对结果的影响方向。比如压测环境的SSD性能比生产低一档,那你心里要有数,压出来的TPS是偏保守的,生产在同等情况下只会更好。
性能测试环境是一门“基建”活,你前期投入越多,后续每次压测的产出就越扎实。环境搭得草率,后面无论脚本写得多好、分析能力多强,都建立在流沙之上。这套逻辑我反复在团队里讲,希望在读的你也少走这些弯路。