我做过不少压测,也见过不少压测翻车的现场。印象最深的一次,是某系统压到 2000 TPS 就再也上不去,研发说代码没问题,运维说配置没问题,最后我把应用容器的线程池从默认值调到 400,TPS 直接翻倍。从那以后我就明白,性能测试从来不是拿工具拉一个并发、再等一个数字报表那么简单。它至少要覆盖目标、工具、环境、脚本、监控、结果、复盘多个视角,否则一次压测很可能变成一次数据造假的彩排。
这篇不打算写任何一款工具的官方文档式教学,而是把性能压测整个链路里我踩过的坑、验证过的经验、沉淀下来的方法,按多个视角一次性说透。无论你是刚接触压测的测试同学,还是被性能问题追着跑的接口开发,或是想给系统做个全面体检的运维,应该都能从中找到能直接用的东西。
1. 目标视角:压测之前先想清楚要验证什么
1.1 性能测试本质上是为了回答四个问题
很多人一提到压测就默认是“把并发拉高,看系统什么时候挂”,但实际工作中,性能验证的目标远不止这一种。为了避免大家拿着一套脚本走天下,我把常见诉求拆成了四类,每一类的结论形式和执行方式完全不同。
第一类是基准测试,也叫基线测试。它的目标是确定系统在特定配置、特定业务量下“原本”的表现,通常用少量并发甚至 1 个用户去跑,得到一组响应时间、吞吐量的基准值。这类测试最大的价值在于给后续调优和回归提供参照物,没有基线,后面所有“优化了 30%”的说法都是自说自话。
第二类是负载测试,目标是验证在预期的正常负载下,系统能不能稳定满足 SLA。比如业务高峰期 5000 人同时在线,峰值 TPS 大概是 800,那就模拟这个负载去跑一段时间,看各项指标是否达标。它回答的是“够不够用”的问题。
第三类是压力测试,目标是找到系统的上限和崩溃点。通常会让并发或吞吐持续加大,直到系统出现错误率上升、响应时间恶化的拐点,然后记录这个拐点在哪。它回答的是“能撑到什么时候”的问题,用于容量规划、限流阈值设定和应急预案制定。
第四类是稳定性测试,也叫疲劳测试。目标是以一定的负载持续运行数小时甚至数天,观察内存是否泄漏、连接是否耗尽、性能是否逐渐劣化。很多系统能扛住 1 小时的压力,却扛不住 8 小时的温水煮青蛙,这类测试就是我们日常说的“跑长测”,我最怕听到的一句话就是“系统没问题,只是跑了 20 分钟就停了”。
除了这四类,还有尖峰测试,模拟突发流量瞬间暴涨的场景,比如整点秒杀、定时任务触发瞬间。这类测试关注的不是能不能压死系统,而是系统被瞬间冲高后能否自愈、能否通过限流和熔断保住核心链路。
我的建议是,接到压测需求后,先不要急着写脚本,而是问一句:这次的目标是验证容量、找上限、确认稳定,还是对比调优效果?目标不一样,并发模型、压测持续时间、监控重点全部都不一样。把这件事搞清楚,整个过程就成功了一半。
1.2 指标不是只看吞吐和平均响应时间
压测报告里最常见的两个数字是 TPS 和平均响应时间,但这两者单独拿出来都非常有迷惑性。
TPS(每秒事务数)衡量的是系统的吞吐能力,QPS(每秒查询数)经常和它混用。严格来说,一个事务可能包含多个查询,比如“下单”事务包含库存查询、订单写入、支付调用等多个内部操作。在制定目标时,一定要先把口径定清楚,到底 TPS 是指完整的业务流程数,还是指单个接口的请求数。口径不统一,研发和测试对同一个数字的理解可能完全不同。
响应时间方面,我强烈建议把平均响应时间放到次要位置,重点看百分位指标。一次压测中,大部分请求在 100ms 内返回,但少量请求卡了 3 秒,平均值可能只有 150ms,看起来非常健康,实际用户体验已经崩了。更准确的做法是看 TP95、TP99,也就是 95%、99% 的请求响应时间落在什么范围内。P99 最能反映长尾延迟,而长尾延迟往往就是性能问题爆发的信号。
并发用户数和系统承受的压力也不是直接等号。10000 人在线,不等于 10000 并发;在一个典型业务系统中,在线用户可能只有 5% 到 10% 同时处于操作状态,而这部分里真正在发请求的又会打一个折扣。有一个实用的估算公式(利特尔定律):并发数 = 每秒请求数 × 平均响应时间。如果目标 QPS 是 1000,平均响应时间是 100ms,那么系统实际承载的并发大约是 1000 × 0.1 = 100。很多不懂的人一上来就把并发线程数拉到 2000,其实系统根本不需要也不应该这么扛。
1.3 压测目标必须是可以验收的书面约定
压测之前,我会逼着需求方把目标写成一条一条可量化的语句,而不是笼统地说“性能要好”。一个标准的性能目标包含四要素。
- 吞吐目标:核心接口 TPS 需要达到多少
- 延迟目标:TP95、TP99 不能超过多少毫秒
- 成功率目标:错误率上限是多少(一般不超过 0.1%)
- 资源水位目标:压测期间 CPU、内存、磁盘 IO 平均占用不高于多少
举个例子,订单提交接口:“在 800 并发下,TPS ≥ 1500,TP99 ≤ 500ms,错误率 ≤ 0.1%,压测持续 1 小时,应用服务器 CPU 平均 ≤ 70%,数据库 CPU 平均 ≤ 50%。”这样一份目标书,既能让研发清楚优化方向,也能在最终评审时避免两边对着完全不同的数据吵架。
2. 工具与环境视角:结果可信度从准备阶段就决定了
2.1 主流压测工具到底怎么选
每个团队都有自己的压测工具偏好,这很正常,但选型不应该只看“用过的人多”,而要结合自己的协议、脚本复杂度、分布式需求和团队技能来决定。
我用一张表把常见工具的核心差异拉出来:
| 工具 | 脚本语言 | 并发模型 | 分布式支持 | 学习成本 | 最擅长的场景 |
|---|---|---|---|---|---|
| JMeter | 图形化 + 少量脚本/BeanShell | 线程模型 | 支持(多施压机) | 中低 | 复杂业务链路、多协议混合场景 |
| wrk | Lua | 基于事件驱动的高性能模型 | 有限 | 低 | 单接口快速压测、开发自测 |
| k6 | JavaScript | 基于 Go 的高并发模型 | 支持(云/集群) | 中 | 脚本化、CI/CD 集成场景 |
| Locust | Python | 协程模型 | 支持 | 中 | 复杂自定义协议、需要 Python 生态 |
| Gatling | Scala | Actor 模型 | 支持 | 中高 | 对性能要求高的 DSL 场景 |
选型核心要看三点。第一,协议是否覆盖。纯 HTTP 接口哪个工具都行,但涉及 WebSocket、gRPC、自定义 TCP 二进制协议时,JMeter 可以通过插件扩展,Locust 可以直接用 Python 写客户端,而 wrk 这种轻量工具就很难做。第二,场景复杂度。单接口、链路短、参数少的场景,用 wrk 这种轻量工具效率极高;多接口、有依赖、需要动态取值、有业务比例配比的场景,JMeter 这类重工具反而更省心。第三,报告与集成能力。k6 和 Gatling 对自动化流水线支持更友好,JMeter 则靠庞大的插件生态弥补了原生报告不够好看的问题。
2.2 我的组合打法:JMeter 为主,wrk 为辅
在团队里我没有让所有人都用一种工具,而是按场景配置了“重型”和“轻型”两套方案。
正式压测、链路复杂、需要留痕的项目,我基本都用 JMeter。原因很实际:社区生态完整、团队里每个人都能看懂测试计划、官方的聚合报告和 HTML 报告可以直接存档,遇到问题容易排查。但 JMeter 有个致命弱点,就是以线程模型来模拟并发,线程开多了之后施压机本身会先吃不消,所以真到高并发场景,不能指望一台笔记本跑出准确结果。
日常开发自测和快速验证瓶颈,我强烈推荐 wrk。它用事件驱动代替线程,单机就能打出很高的并发,写一个 Lua 脚本几十行就能完成带自定义参数的压测。它跑出来的延迟分布非常直观,适合在开发环境先摸摸底。
这里有一个我在 JMeter 上踩过的大坑:千万不要开着图形界面的监听器跑压测。GUI 模式本身会消耗本机资源,监听器里的结果树如果开启“保存响应数据”,还会把每一步的完整响应写到磁盘,压测结束时施压机可能已经成了系统瓶颈。正确的做法是用命令行模式执行,测试期间只开启必要的输出配置,跑完再用结果文件生成 HTML 报告。
命令行示例:
jmeter -n -t order_stress.jmx -l result.jtl -e -o html_report如果并发规模很大,比如单机压到 3000 线程还达不到目标,就需要分布式压测。多台施压机通过 agent 模式配合调度,但要注意同步参数、确保每台 agent 的 JDK 版本、JMeter 版本、脚本和数据文件完全一致,否则同一场景在不同施压机上跑出来的数据会有明显偏差,合并统计结果时误差会被放大。
2.3 测试环境与测试数据,是压测结果可信度的地基
环境问题是我见过压测结果无法复现的主要原因。很多团队直接用开发环境压,数据量极少,代码逻辑也不完全一致,压出来的数据除了证明“系统能跑”没有其他意义。
最理想的做法是准备一个独立于日常开发的预发环境,规格尽可能贴近生产。重点不是一模一样,而是以下关键点不能差太多:应用实例数和对应的注册中心流量比例、数据库配置和连接池上限、缓存分片和容量、消息队列的消费者实例数。如果生产是 8 个应用实例,测试环境至少要有 2 到 3 个实例,否则单实例的线程池和连接池压力放大效果无法体现。
测试数据同样重要。数据库中如果只有几万行数据,和几千万行数据时,相同 SQL 可能走向完全不同的执行计划,压测结果可能差出好几倍。我的习惯是至少准备与生产核心表数据量为 50% 到 100% 的测试数据,并且数据分布要模拟真实业务。比如用户 ID 要覆盖低、中、高不同热度,订单状态要有多种类型,不然很容易遇到“压测全是缓存命中”的虚假繁荣。
还要重点处理数据隔离和幂等。压测会不断写入数据,如果脚本里的手机号、订单号固定不变,第一次能成功,第二次可能就触发唯一索引或幂等校验直接报错。我通常在参数里拼接时间戳和随机数做前缀,并在请求头加一个自定义标签,例如x-perf-flag: 1,这样后续去掉压测数据时,只要按这个标记清理即可,不影响正常业务数据。
3. 场景与脚本视角:把用户真实行为翻译成压测请求
3.1 场景模型设计要遵循渐进原则
很多人拿到 JMeter 就直接把线程组的并发数拉到 500,这种方式我极度不推荐。合理的压测场景设计应当从轻到重、从单点到链路,循序渐进,这样每个阶段都能留下可解释的数据。
我的习惯分三步走。第一步,先做单接口基础测试。用一个线程组、少量并发跑 5 分钟,验证脚本本身的正确性,确保参数、断言、数据都能通过,同时拿到单接口的基线和大概的吞吐上限。第二步,按业务比例混合接口压测。把登录、列表查询、详情、提交订单等接口放在同一个线程组或事务控制器里,通过加权比例模拟真实用户操作。第三步,做阶梯加压,也就是 ramp-up。比如初始 50 并发,每 5 分钟增加 50,同时记录每一阶梯的 TPS、RT、错误率,这样能非常清楚地看到从哪个并发点开始出现拐点。
这里有一个很容易忽略的细节:思考时间不能设置成 0。把思考时间设成 0,相当于所有用户在同一瞬间抢系统资源,这属于尖峰测试的玩法,不是真实负载。真实用户操作之间会有停顿,这个停顿对系统的压力是重要的缓冲。我会根据业务特点设置 100ms 到 500ms 不等的思考时间,既保证压力足够,又能接近真实流量。
3.2 参数化和关联:脚本的真实性决定了结果的有效性
压测脚本里最影响真实性的就是参数处理。如果所有请求都使用同一个 token、同一个用户 ID、同一个商品 ID,那压测出来的结果至少有两个问题:一是热点数据缓存会让后面的请求全部命中缓存,系统负载远低于实际;二是并发场景下同一个用户共享会话,可能出现资源竞争导致的非典型错误。
参数化最常用的是 CSV 数据集,把一批测试用户、商品、订单号放在文件里,让每个线程循环取不同值。另一个是动态关联,遇到登录返回的 token、创建订单生成的订单号这类前后依赖,需要使用正则表达式或 JSON 提取器把返回值取出来传给后续请求。拿订单提交流程举例,先请求创建订单接口,从响应中提取orderId,再把orderId带入支付接口的请求体里,这个过程就是关联。
还有一个让很多人忽略的点:对随机数据要有一个清醒的认识。不是所有字段都必须随机,但标识性字段、账号类字段、空间查询的坐标字段等,最好都要覆盖不同取值。比如列表查询接口,如果每次都用同一个 keyword 查询,数据库可能走了缓存或者命中同一索引段,压测结果对搜索引擎或数据库优化没有任何参考价值。
3.3 脚本里的隐形杀手:断言、监听器和事务定义
写脚本时我还见过不少“压测没到量,工具先挂了”的情况,问题往往出在三个地方。
第一,断言过重。每个接口都加了响应体包含校验,每次请求都要做字符串匹配,引擎额外承担大量解析工作。我的原则是,压测脚本里断言只保留最关键的成功标志,比如状态码 200 或业务码为 0,其余细枝末节的字段校验放到自动化测试去做,压测脚本只负责制造压力。
第二,监听器滥用。保存日志、保存全量响应、开启结果树,这些功能都会产生巨大的 IO 开销。线上压测我通常只保留一个简单的聚合结果写入文件,跑完一次性生成报告。
第三,事务边界定义错误。JMeter 里的“事务”默认是单个 Sampler,但在真实业务里一次下单操作可能包含创建订单、支付、库存扣减多个步骤。要在事务控制器里把这几个 Sampler 包起来,统计出整个业务链路的 TPS 和响应时间,而不是只看单个接口。
4. 监控视角:压测时不看监控,结果就是盲人摸象
4.1 四层监控体系:应用、中间件、数据库、操作系统
压测过程中,压力客户端给出的 TPS 和错误率只是结果,真正能帮我们定位原因的是过程中各层的运行状态。我习惯把监控分成四层,缺一层都不够完整。
应用层要看 JVM 的堆内存、GC 次数和停顿时间、活跃线程数、Tomcat/容器连接线程池的使用情况。盈利性监控可以用 APM 工具,比如 SkyWalking 之类的开源链路追踪系统,但哪怕是临时用jstat -gcutil <pid> 1000这种命令盯一下 Full GC 次数,也比什么都不看强。
中间件层要盯缓存命中率、连接数、队列积压量。比如 Redis 的INFO stats可以看到 keyspace_hits 和 keyspace_misses,消息队列的 consumer 堆积情况直接反映了消费能力是否跟上生产。
数据库层是最容易忽略也最能暴露问题的一层。重点看慢查询数量变化、连接数是否快速增加、锁等待次数、InnoDB 缓冲池命中率,以及当前活动的 SQL 是什么。命令可以先从SHOW FULL PROCESSLIST开始,但这只能看到瞬间状态,如果要连续观察,用performance_schema.events_statements_summary_by_digest按 SQL 摘要聚合分析更可靠。
操作系统层关注 CPU、内存、磁盘 IO 和网络。vmstat 1可以看 CPU 的 us/sy/wa 占比,iostat -x 1可以看到磁盘利用率,sar -n DEV 1看网卡流量。这些数据的抓取频率建议在压测期间保持 5 秒一次,可以和压测脚本的阶梯并发度对齐,方便事后做关联分析。
4.2 分链路追踪一次请求的时间都花在哪里
压测出现响应时间恶化时,第一步不是调代码,而是搞清楚这 500ms 到底消耗在哪一段链路上了。一次完整的用户请求可能经历网关、应用、下游服务、缓存、数据库,每一跳都可能成为瓶颈。
如果有链路追踪系统,直接看 trace 的时间分布即可。如果没有,最笨也最有效的办法,是在应用代码里对关键调用点打点计时。比如发现某接口响应时间从 200ms 涨到 1200ms,可以通过分段时间拆解看出是本地排队时间长、下游服务调用慢,还是数据库查询变慢。
我处理过一类非常典型的案例:某接口 TP99 从 300ms 突然涨到 3 秒,但从应用日志看接口本身的逻辑执行时间只有 40ms。后来一查,是应用线程池被打满了,大量请求在线程池队列里排队等待,而排队的时间完全没算在接口处理时间内。这就是典型的“线程池耗尽”问题,压力根本没打到逻辑层,全堵在入口。没有合适的监控视角,这类问题很难定位。
4.3 资源利用率的正确解读:饱和度比百分比更重要
很多人看到 CPU 利用率 60% 就觉得没问题,但 60% 的数值含义在单核和多核系统上完全不同。更关键的是,CPU 较高未必是坏事,反而可能说明系统在充分干活;真正要警惕的是“CPU 不高但吞吐已经上不去”,那往往意味着瓶颈在锁、连接池或者磁盘 IO。
判断饱和度的三个实用信号:一是请求排队数或线程池活跃数触顶,比如 Tomcat 线程池打满,大量请求都在等待;二是数据库连接池用满,报connection timeout之类的错误;三是磁盘 IO 利用率长时间在 80% 以上,伴随iowait升高。看到这些信号,就算系统 TPS 还没下降,也已经到了容量上限附近,继续加压就是在放大延迟。
5. 分析与调优视角:从数字报表到真实瓶颈
5.1 学会看响应时间分布和 TPS 曲线的形态
拿到压测报告后,第一眼要看的是 TPS 和响应时间随并发变化的关系曲线,而不是看单个最大值。
正常情况下,随着并发从低到高,TPS 会先呈线性增长,然后增速放缓,进入平台期,最后出现下降。平台期的起点就是系统的合理容量点,而下降点往往是资源耗尽或排队加剧的表现。响应时间曲线也一样,前期平缓,到了某个并发点后会快速抬头甚至垂直上升,这个拐点就是需要重点记录的阈值。
如果 TPS 在某个并发下突然断崖下跌,同时错误率暴增,大概率是触发了某个保护机制,比如限流、熔断或连接池拒绝。如果 TPS 没有下降但响应时间持续升高,通常说明请求在排队,系统还在尽力处理,但已经积压了大量任务。这些形态对应的问题和调优方向完全不同,拿到曲线后先判断属于哪种形态再动手。
5.2 常见底层瓶颈:线程池、连接池、GC、锁
很多性能问题无非是几种经典的底层原因在轮番上演。
线程池过小是最高频的瓶颈。容器默认线程池参数往往保守,当并发超过线程池容量时,新请求只能在队列里排队。这类问题的特征是:CPU 利用率不高、TPS 提不上去、响应时间持续恶化,解决方法大多是调大 maxThreads 到合理值,但调太大也会导致上下文切换开销增加,通常按每核 200 到 300 线程做初步估算后实测调整。
连接池耗尽也是经典问题。数据库连接池、Redis 连接池、HTTP 客户端连接池任何一个打满,都会导致获取连接超时。这类问题的特征是:应用日志频繁出现获取连接超时或等待连接异常,同时数据库或对应中间件的当前连接数等于池上限。解决方向是调大池大小,但更要排查是否有连接泄漏,比如某个异常分支没有归还连接。
GC 引起的性能问题往往呈周期性的响应时间尖刺。我见过一个系统,大多数请求响应时间 200ms,但每隔几秒就有一个请求冲到 2 秒以上,监控 GC 后发现每次 Full GC 正好对应尖刺。这时候优先处理的是对象分配速率和堆内存参数,而不是盲目优化业务代码。
锁竞争比较隐蔽,但一旦出现就是压测结果惨不忍睹。热点数据加锁、synchronized 代码块包含耗时操作、分布式锁长时间持有,都会导致并发能力急剧下降。通过线程 dump 经常可以看到大量线程处于 BLOCKED 或 WAITING 状态。
5.3 一个完整调优循环的实操记录
我通常会用“压测—分析—调优—再压测”的循环,确保每一次改动都能用数据说话。
之前有个某接口的性能问题,初始压测 200 并发下,TPS 只有 300,TP99 高达 1300ms。第一轮分析后,发现应用线程池满负荷,但数据库和 CPU 压力都很低,于是把应用线程池从默认值调大,TPS 提升到 800,TP99 降到 700ms。第二轮发现数据库慢查询明显增多,检查发现压测数据里某个表的索引没有命中,补上联合索引后再压,TPS 稳定在 1300,TP99 降到 420ms。整个过程每轮控制变量,只改一个东西,递进很快。
这里最关键的是控制变量。如果同一轮里既调了线程池,又改了数据库索引,还重启了服务,最后出了问题都分不清是哪一步生效的。我的习惯是每轮只做一处变更,同时保证脚本、数据、并发、环境完全一致,压出来的差异才可信。
6. 常见问题与复盘视角:那些压测翻车现场
6.1 最容易导致结果废掉的六个坑
我把这几年压测过程中遇到过的问题集中梳理了一下,其中以下六条是出现频率最高、对结果影响最大的。
第一,施压机被打满。单台机器跑 JMeter 高并发时,施压机本身的 CPU 或网络先到瓶颈,测出来的结果根本反映不了服务器真实能力。判断方法是压测结束后看施压机的资源消耗,如果 CPU 连续超过 80%,就要考虑扩容施压机或用分布式模式。
第二,测试数据过于单一。固定账号命中缓存、固定订单号触发幂等、单一商品 ID 导致数据库行锁竞争,都会让结果失真或出现假错误。
第三,预热缺失。应用刚启动时 JIT 还没有充分编译,连接池还是空的,直接高并发压测会让首批请求承担大量初始化开销,表现偏慢。正式压测前先用较低并发跑 5 到 10 分钟做预热,让系统进入稳定状态。
第四,压测流量混淆。重要环境压测没有做流量标记,结果压测数据和线上数据混在一起,事后清理困难,还可能误伤线上统计报表。我在压测请求头加标记之后,这个问题基本绝迹。
第五,并发模型不符合真实场景。所有接口的 Think Time 都设 0,所有接口权重一样,算出来的结果和用户真实行为完全对不上。这个在前面章节已经展开说过。
第六,压测环境变更未冻结。压测期间研发合入了新代码、运维改了连接池参数、DBA 建立了新索引,都会让结果失去可比性。压测前发一个“环境冻结通知”,让相关人员在压测期间不做变更,能避免大量扯皮。
6.2 稳定性测试接下来的独特看点
短时间高并发压测通过,不代表系统能安稳跑一天。稳定性测试需要额外关注几个短测不会暴露的指标。
内存泄漏是最隐蔽的。如果压测过程中 TPS 平稳但堆内存使用率一路爬升,每次 GC 后内存回收幅度逐渐减小,十有八九是某处对象被错误持有不释放。这类问题用jmap -histo:live <pid>看对象增长趋势很有帮助。
连接泄漏也是稳定性问题大户。每次请求都会新开连接但不关闭,最终所有连接池被占满,系统卡死。监控连接数随时间缓慢增长时,要检查代码里所有获取连接的路径是否都有 finally 释放逻辑。
日志和临时文件膨胀同样值得警惕。如果应用打印了 DEBUG 级别的日志,稳定测试跑 12 小时下来,日志文件可能膨胀到几十 GB,磁盘写满后系统会进入半死状态。稳定性测试期间必须对磁盘使用率做实时告警。
6.3 压测过程的操作纪律
最后聊几条压测现场的操作纪律,是多年踩坑换来的。
第一,压测开始前发一个明确的通知,包含压测时间窗口、涉及的系统、入口地址、预期并发、受影响的上下游,让大家有个心理预期。我曾经遇到过压测和另一个团队的定时任务撞车,两边同时加压,结果互相干扰,数据完全没法看。
第二,压测执行要有“紧急止损”路径。如果压测过程中发现监控指标异常,尤其是有依赖的中间件严重告警,立即降并发或停止压测,不要为了数据硬扛。提前写好事后停止的脚本,可以在 30 秒内把一个高并发场景彻底停掉。
第三,每次压测的配置文件、脚本文件、数据文件、结果文件、监控截图一并归档。几个月后再有人问“上次压测结论是什么”,能快速调用当时的环境和脚本,比靠记忆靠谱得多。
写在最后
做了这么多年性能压测,我的体会有两点。一是性能测试不是一次性任务,而是一个需要反复迭代的过程,系统每经历一次大版本变更、架构调整、数据量增长,之前压测出的结论就可能不再成立,把压测扎进日常研发流程里,比一年做一次单点突击更有价值。二是压测最大的敌人不是系统太弱,而是数据不透明。只要监控数据、脚本数据、环境数据都能完整保留,任何一次性能问题都能快速定位到具体原因。
最后再分享一个小技巧:压测前我会把这次要验证的核心指标和预期值写成一页纸,贴在压测执行的终端旁边。每次阶梯加压结束,先看这页纸上的指标有没有达成,再去做复杂分析。这样既不会迷失在大量图表里,也能保证每一步都是朝着最开始定下来的目标推进。哪怕最终压测结果不理想,这一页纸也能成为和研发、产品对齐问题的最有力证据。