性能测试:性能测试工程师到底在测什么
一提到性能测试,第一反应就是:
- 并发打多大
- QPS 跑多少
- 平均响应是不是低于 1 秒
- 工具用 JMeter 还是别的
这些问题当然重要,但如果性能测试只停留在这一层,它最后通常只会变成一场“把流量打上去然后看几张图”的活动。
真正有价值的性能测试,应该回答的是一组更接近系统决策的问题:
- 系统在什么负载区间内是健康的
- 从哪里开始退化,退化是平滑还是断崖
- 哪一层最先限制系统继续增长
- 这个限制会不会扩散成更大的系统性风险
- 当前架构的哪些默认假设已经开始站不住
- 如果业务量继续增长,下一步最该补的能力是什么
所以“性能测试工程师到底在测什么”这个问题,本质上不是在问测试工具,也不是在问指标名词,而是在问:
性能测试这件事,到底在为团队识别什么风险、提供什么判断、支撑什么决策。
一、性能测试测的不是“压”,而是系统在负载下的行为变化
性能测试经常被直接等同于“压测”。
这个理解不能说错,但太窄了。
因为“压”只是手段,真正重要的是:
系统在不同负载强度、不同持续时间、不同业务结构下,会表现出什么行为。
这里面至少有三层含义。
1. 不是只看能不能扛住
系统不是只有“正常”和“挂掉”两种状态。
真实世界里,更常见的是一条退化曲线:
- 一开始完全健康
- 然后尾延迟慢慢变差
- 再接着错误率开始抬头
- 再往后吞吐不再增长
- 最后进入明显不稳定区
如果测试只看“最后挂没挂”,那很多真正有价值的信息都会被错过。
2. 不是只看单点接口
很多性能问题不是单个接口本身慢,而是系统链路在负载下开始相互拖拽。
例如:
- 一个查询接口慢,拖住应用线程
- 线程池堆积,连带其他接口也慢
- 缓存 miss 放大数据库压力
- 下游依赖抖动,让上游重试流量继续放大
这说明性能测试往往不是在测某个接口的快慢,而是在测整个系统如何在压力下相互影响。
3. 不是只测峰值,还要测持续性
有些系统瞬时看起来非常能扛,但一旦持续 30 分钟、1 小时、2 小时以后,问题才开始出现:
- 内存缓慢上涨
- 连接不释放
- GC 变差
- 磁盘写入开始堵
所以性能测试测的也不是“瞬时爆发能力”这么简单,而是系统在持续负载下是否还能稳定工作。
二、从本质上说,性能测试测的是“资源竞争”和“退化传播”
如果把性能问题再抽象一点,大多数问题最终都绕不开两件事:
- 资源竞争
- 退化传播
1. 资源竞争
系统在低流量时,很多资源都显得“够用”:
- CPU
- 内存
- 数据库连接
- Redis 连接
- 线程池
- 网络带宽
- 磁盘 I/O
但一旦业务量上来,资源之间开始激烈竞争,系统原本隐藏的边界就会被快速暴露。
2. 退化传播
更麻烦的是,性能问题很少只停在一个点上。
例如:
- 数据库变慢
- 应用线程被拖住
- 请求开始堆积
- 网关超时上升
- 重试又把压力加回系统
这时候问题已经不是“数据库慢”,而是开始向整条链路扩散。
性能测试的价值,就在于识别这种扩散是怎么发生的。
三、性能测试工程师真正要回答的,不是一个数字,而是一组边界
最常见的一句问题就是:
“这个系统最多支持多少并发?”
这个问题表面上很合理,但如果最后只给出一个数字,通常价值并不高。
因为系统能力不是单点值,而是一组条件下的能力边界。
它至少会受到这些因素影响:
- 请求类型
- 数据规模
- 数据冷热
- 读写比例
- 持续时间
- 缓存命中率
- 下游依赖状态
所以更有价值的性能结论,通常不是:
- 最大支持 800 并发
而是更像:
- 在 400 并发以内系统处于健康区
- 500 并发开始尾延迟明显恶化
- 650 并发后数据库连接池等待上升,系统进入风险区
- 800 并发只是极限冲高,不应作为安全容量
这种边界式结论才真正有决策意义,因为它能告诉团队:
- 安全工作区在哪里
- 风险从哪里开始
- 极限区大概在哪
四、性能测试测的不只是“快不快”,更重要的是“稳不稳”
过于关注平均响应时间,这是很典型的问题。
因为真实用户感知到的,很多时候不是平均值,而是:
- 抖不抖
- 卡不卡
- 偶发请求是不是特别慢
一个系统即使平均值很好看,也可能在真实体验上很差。
例如:
- Avg 200ms
- P99 5s
这类系统看报表好像挺漂亮,但线上用户一定会感受到明显问题。
所以我一直认为,性能测试工程师测的一个关键对象,就是系统的“稳定性质量”:
- 尾延迟稳定不稳定
- 同样压力下重复执行是否一致
- 长稳压下曲线是否持续恶化
换句话说,性能测试不只是测速度,还在测系统是否可靠。
五、性能测试其实也在测“架构假设”是不是还成立
这一点特别重要,也特别容易被忽略。
很多系统上线时,天然带着一堆没有写出来的默认假设,比如:
- 缓存命中率会比较高
- 某类接口占比不会太大
- 数据量不会突然暴涨
- 某个第三方依赖应该足够稳定
这些假设在平时可能没问题,但业务一变化,它们就可能集体失效。
性能测试一个很高价值的作用,就是去验证这些假设到底还能不能站住。
例如:
- 如果缓存 miss 明显变多,数据库会不会被直接打穿
- 如果促销活动导致写请求暴增,锁冲突会不会快速放大
- 如果查询条件更复杂,搜索链路会不会提前进入尾延迟恶化区
这意味着性能测试不仅是在测当前系统快不快,也是在测:
- 当前架构是不是还适合继续承载业务增长
六、不同类型的性能测试,其实在回答不同问题
把所有性能测试都混在一起叫“压测”,这会导致目标非常模糊。
更倾向把性能测试至少分成下面几类。
1. 容量类
关注:
- 当前资源下最多能支撑多大业务量
- 健康区、风险区、极限区分别在哪里
七、如果要从零开始做一次性能摸底,更合适的落地方式如下
只讲“性能测试在测什么”还是偏抽象。
真到项目里,通常会先把一次最小可执行摸底跑起来,再决定后面要不要做更重的压测。
一个比较实用的落地顺序通常是:
- 先选一条核心链路
- 给出一个业务目标值
- 准备一套最小监控面
- 做一轮分阶段升压
- 对齐退化拐点和资源证据
- 输出健康区、风险区和后续动作
例如一个典型的接口链路摸底,先定成这样:
| 项目 | 实践口径 |
|---|---|
| 目标链路 | 搜索接口 + 详情接口 |
| 核心目标 | 活动高峰 1.2 倍时仍可用 |
| 执行方式 | 100 -> 200 -> 300 -> 400 并发逐段升压 |
| 每段时长 | 10 分钟预热 + 20 分钟正式观察 |
| 必看指标 | TPS、P95、P99、错误率、CPU、数据库连接池等待 |
| 停机线 | 错误率持续 3 分钟超过 1%,或数据库连接池耗尽 |
这里最关键的不是“并发值写多少”,而是每一段都要能回答一个明确问题:
- 这一段系统是不是还在健康工作区
- 哪个指标先开始失真
- 失真是偶发噪音,还是开始形成趋势
八、一次性能摸底里我最少会准备哪些材料
如果连最基本的材料都没准备齐,性能测试很容易变成“流量打上去了,证据没留住”。
通常至少准备下面这些内容:
1. 一张链路图
哪怕很简化也要有,至少画出:
- 流量入口
- 应用服务
- 数据库
- Redis
- MQ
- 第三方依赖
因为后面看到瓶颈时,你需要快速知道它是在主链路、旁路还是依赖层。
2. 一张压测记录表
现场通常会维护一张很朴素的表:
| 阶段 | 并发 | TPS | P95 | P99 | 错误率 | 现象 | 初步判断 |
|---|---|---|---|---|---|---|---|
| stage-1 | 100 | 620 | 180ms | 320ms | 0 | 稳定 | 健康区 |
| stage-2 | 200 | 1180 | 240ms | 520ms | 0 | 稳定 | 健康区 |
| stage-3 | 300 | 1540 | 410ms | 1.1s | 0.2% | 查询偶发超时 | 接近风险区 |
| stage-4 | 400 | 1600 | 920ms | 2.8s | 1.4% | 数据库连接池等待上升 | 风险区 |
这张表的价值很高,因为它把“执行过程”直接沉淀成后续报告的骨架。
3. 一组固定观察窗口
现场通常会明确谁负责看什么:
- 压测侧看吞吐、延迟、错误和脚本异常
- 应用侧看线程池、GC、接口日志
- 数据库侧看连接池、慢 SQL、磁盘等待
- 中间件侧看 Redis、MQ、网关状态
这样现场不容易所有人一起盯同一张 TPS 图。
九、更看重的一套最小排查动作
第一次做性能测试,一出问题就开始“全员找原因”。
这种方式通常很乱。
更常用的最小排查动作是:
- 先确认压测机是否健康
- 再确认错误是不是业务错误还是工具错误
- 对齐吞吐、P99、错误率开始变差的准确时间
- 回看同一时间窗口的应用、数据库和中间件图
- 只抓最早出现异常的那个点先解释
这里有一个很重要的实践原则:
一次性能排查里,先解释“第一个异常”,不要一上来解释“最后系统为什么慢”。
因为最后的慢,很多时候只是前面某个瓶颈扩散后的结果。
2. 稳定性类
关注:
- 长时间运行会不会退化
- 是否有内存泄漏、连接泄漏、缓存抖动、文件句柄耗尽
3. 瓶颈定位类
关注:
- 当前系统最先卡在哪
- 哪一层最先开始限制系统增长
4. 方案验证类
关注:
- 做完架构调整之后收益到底多大
- 继续扩节点是否还有价值
5. 风险评估类
关注:
- 上线、大促、迁移前最大的风险在哪里
- 生产流量上来以后最可能先炸哪一层
如果这些测试目标不区分,最后最容易发生的事就是:
- 工具跑了
- 数据也有了
- 但没人知道这次测试到底是为了回答什么问题
七、在项目里最常见的几个性能测试误区
误区 1:把工具吞吐当成系统吞吐
比如 JMeter 打不上去,有些人第一反应就是:
- 系统扛不住
但真实原因可能是:
- 压测机 CPU 已经满了
- 本地端口耗尽
- 脚本参数化有问题
- 连接复用没配好
性能测试工程师首先要确认的,不是系统有没有问题,而是“施压本身是不是可信”。
误区 2:只看平均响应时间
平均值太容易掩盖真正风险。
很多严重问题都藏在:
- P95
- P99
- 错误率抬头之前的尾部恶化
误区 3:只看接口,不看资源和链路
接口慢只是表象。
如果不看:
- CPU
- 线程池
- 数据库连接
- Redis
- 磁盘
- 网络
那大多数结论都只能停留在“变慢了”,无法进入“为什么慢”。
误区 4:用不真实的流量模型,得出很真实的结论
例如:
- 所有请求都用同一组参数
- 没有冷热数据差异
- 没有读写比例变化
- 没有真实业务链路组合
这种测试即使结果很漂亮,也不一定有实际价值。
误区 5:压测完只给数字,不给解释
例如只说:
- 支撑 1000 并发
- 错误率 2%
但不说:
- 为什么从这个点开始退化
- 哪一层是主要瓶颈
- 下一步最该怎么优化
这种结果对团队的真实帮助很有限。
八、性能测试工程师真正重要的能力,不只是会用工具
性能测试能力经常被等同于:
- 会 JMeter
- 会看 TPS
- 会出报告
这只是基础,不是核心。
更关键的是真正重要的能力至少包括下面几类。
1. 场景抽象能力
能把真实业务翻译成:
- 流量结构
- 并发模型
- 数据模型
2. 指标理解能力
能解释:
- 吞吐、延迟、错误率、资源之间的关系
3. 瓶颈归因能力
能从表象一路追到:
- 应用
- 中间件
- 数据库
- 基础设施
4. 风险翻译能力
能把测试结果翻译成团队听得懂的决策语言:
- 能不能上线
- 要不要扩容
- 最先该动哪一层
九、更常用的分析顺序
如果一次性能测试结果出来,通常不会马上先看某一层监控,而是按下面顺序走。
1. 先确认施压是否可信
确认:
- 压测模型是否接近真实
- 压测机是否先成瓶颈
- 脚本是否有明显失真
2. 再看系统外部表现
确认:
- 吞吐
- P95 / P99
- 错误率
3. 再看资源与依赖
确认:
- CPU
- 内存
- 数据库
- Redis
- 磁盘
- 网络
4. 最后再做因果判断
也就是回答:
- 谁最先开始限制系统继续增长
- 问题如何向外扩散
结语
性能测试工程师真正测的,不是“并发打多大”,也不是“某个工具会不会用”,而是系统在负载下的真实行为、退化方式、风险边界和架构假设。
更准确地说,一次真正有价值的性能测试,至少要回答清楚下面几件事:
- 系统在什么区间是健康的
- 从哪里开始退化
- 谁最先限制系统继续增长
- 当前架构的哪些假设开始站不住
- 下一步优化最该投到哪里
如果这些问题回答不出来,性能测试就很容易退化成一次“把流量打上去然后截图”的动作;
如果这些问题能回答清楚,它就会真正成为容量规划、架构优化和上线决策的重要依据。