☰
性能测试指标评估与通过标准:TPS、P95、JMeter实战
2026/9/30 13:40:53 网站建设 项目流程

1. 性能评审会上最容易吵翻的三个问题:口径、分位、阈值

我参加过不少性能评审,真正吵起来的从来不是"工具用 JMeter 还是 LoadRunner",而是同一份报告,测试同学说"我们压到 400 TPS、平均响应 80 毫秒,可以上线",运维同学立刻接一句"CPU 都顶到 90% 了,这也叫能上?"两边说的都是实话,但结论完全相反。问题不在数据,在于彼此嘴里的"TPS""响应时间""通过"根本不是同一套定义。

后来我慢慢总结出一个习惯:任何一场性能评审开始之前,先问三句话。第一句,你的 TPS 是接口级、事务级还是业务级统计口径?第二句,响应时间你看的是平均值还是P95/P99 分位数?第三句,这次的"通过标准"是谁定的、写在哪份文档里?这三句话答不上来,那份报告基本就是一张纸——数字再漂亮,也没法用来做上线决策。

本文想聊的就是这三件事:常用性能指标到底有哪些、这些指标该怎么评估、性能测试的通过标准该怎么定。不管你是刚开始接触性能测试、准备用 JMeter 跑第一轮压测的同学,还是已经跑了几轮但对"达标与否"始终心里没底的中级工程师,这里面的坑和判断逻辑都值得过一遍。顺带说一句,性能测试这个领域的工具迭代很快,但指标体系和分析方法十几年来几乎没变过——工具只是采集器,真正决定结论质量的永远是你有没有把口径锁死。

我见过最典型的失败案例是这样的:一个订单查询接口,压测报告写着平均响应 120 毫秒、TPS 320、错误率 0%,结论"性能达标"。上线第二天用户投诉卡顿。回头翻日志才发现,那 0.5% 落到 5 秒以上的慢请求全被平均值稀释掉了,而那部分请求正好集中在运营最常用的"多条件组合查询"上。平均值把一个右偏分布抹平成了正态分布,这是性能分析里最贵的错觉。

2. 别把指标堆成一锅粥:四层指标体系与各自的采集位置

性能指标最容易犯的错是"全都想要"。报告里塞了几十个数字,CPU、内存、RT、TPS、GC 次数、慢 SQL、缓存命中率……看完之后没人说得清到底哪里有问题。我的做法是按观测距离分层:离用户越近的指标越优先看,离用户越远的指标只在定位瓶颈时才深挖。这四层各有各的采集位置,混在一起看就是自找麻烦。

2.1 第一层:用户体感层,决定"能不能上"

这一层只关心用户真实感受到的东西:页面/接口首字节时间(TTFB)、完整响应时间(RT)、报错和超时的比例、前端资源加载耗时。它是最有话语权的一层,因为老板和业务方只认这个。但它的采集位置也最容易被搞错——如果你只在压测机侧统计 RT,那你测的是"网络+服务端"的总和;如果只统计服务端内部耗时,那你漏掉了网络传输和排队等待。做接口压测一般看施压端侧的时间戳差,做前端性能则必须上真实浏览器或合成监控。

这一层我通常会额外看一个指标:慢请求占比。定义很简单,响应时间超过某个业务可接受上限(比如 1 秒)的请求数占总请求数的比例。它的价值在于,它比 P95 更直观——"每 100 个用户里有 3 个要等 1 秒以上",业务方一听就懂。

2.2 第二层:应用与接口层,定位"哪里慢"

这一层是工程师的主战场:事务 TPS、接口 QPS、响应时间分布、错误码分布、线程池活跃数、连接池等待数、JVM 堆使用与 GC 停顿。采集位置在应用进程内部——APM 的探针、JMeter 的事务控制器、Spring Boot Actuator、或者自己埋点。

我特别想强调连接池等待这个指标。很多"响应时间突然变长"的现场,根因不是服务本身算得慢,而是下游数据库连接池被打满,请求在拿连接这一步排队。这个等待时间通常不会体现在业务方法的执行耗时里,如果你只看方法级耗时,就会得出"服务端处理很快,问题在网络"的错误结论。

2.3 第三层:中间件与依赖层,看清"外部账"

数据库、缓存、消息队列、第三方接口,这一层的指标包括:DB 的 QPS/TPS、慢 SQL 数量与耗时、锁等待、主从延迟、缓存命中率、大 key 数量、MQ 消费延迟与堆积量、第三方接口的 RT 与错误率。压测时最怕的就是被测服务一切正常,但数据库 CPU 已经 100%——那说明你的容量瓶颈在依赖方,服务本身再优化也是白费。

2.4 第四层:主机与资源层,最后一道兜底

CPU 使用率(区分用户态/系统态/IO 等待)、内存与 Swap、磁盘 IOPS 与利用率、网络带宽与重传率、文件描述符与端口占用。这一层的作用不是"发现问题",而是"验证猜想"。当你怀疑瓶颈在磁盘时,去看 iowait;怀疑在 GC 时,去看 GC 日志的时间分布。

层级关注指标典型采集位置什么时候必须看
用户体感层RT、TTFB、错误率、慢请求占比施压端、浏览器、合成监控每一轮都必须看
应用接口层TPS、RT 分布、线程池、连接池、GCAPM、Actuator、JMeter 监听器每一轮都必须看
中间件依赖层DB QPS、慢 SQL、缓存命中率、MQ 堆积数据库监控、缓存面板、MQ 控制台应用层指标异常时优先看
主机资源层CPU、内存、IOPS、网络、句柄系统监控、node_exporter 类工具定位瓶颈时验证用

提示:四层指标不要并列写进报告结论。报告结论只写第一层和第二层的判定结果,第三、四层放在"瓶颈分析"章节里作为证据,否则很容易把读者带偏。

3. 几个核心指标的准确定义:RT、TPS、并发、错误率

指标名字人人会念,但口径经常是错的。这一节我把最常用的四个指标拆开讲,每个都配上计算方式和常见误读。

3.1 响应时间:把平均值换成分位数

平均响应时间 = 所有请求响应时间之和 / 请求数。它的致命缺陷是对长尾极不敏感。假设 1000 个请求里,995 个耗时 50 毫秒,5 个耗时 5 秒,平均值只有约 74.75 毫秒,看起来非常健康,但那 5 个用户已经处于不可用状态了。

正确做法是看分位数。P95 = 500 毫秒的意思是:95% 的请求在 500 毫秒内完成,剩下 5% 超过这个值。分位数不受极端值干扰,又能真实反映尾部体验。我一般会同时报三个数:P50(中位数,代表典型用户)、P95(代表偏慢的那批人)、P99 或 Max(代表最差体验和潜在故障)。

分位数的计算方式不止一种,不同工具结果会有细微差异。常用的线性插值实现大概是这样:

def percentile(values, p): """p 取值 0~1,例如 0.95 表示 P95""" if not values: return None s = sorted(values) if len(s) == 1: return s[0] k = (len(s) - 1) * p lo = int(k) hi = min(lo + 1, len(s) - 1) return s[lo] + (s[hi] - s[lo]) * (k - lo)

注意:报告里报分位数时,一定写清楚是哪个工具算的、样本量多大。样本量小于 100 的时候,P99 基本没有参考意义——一个样本就能让结果翻倍。

3.2 TPS、QPS 与吞吐量:别看总量,看统计窗口

TPS 通常指每秒完成的事务数,QPS 指每秒完成的查询/请求数,吞吐量则是一个更宽泛的概念,可以是每秒字节数、每秒消息数。三者最容易被混淆的地方在于**"事务"的边界**:一个下单流程可能包含 5 个接口调用、3 次数据库写入,它算 1 个事务还是 5 个请求?这个边界必须在压测方案里写死,否则测试和运维对不上账。

另一个隐形问题是统计窗口。TPS = 完成请求数 / 统计时长,如果统计时长取整个压测周期(比如 30 分钟),那么加压阶段和稳定阶段的差异就被平均掉了。我习惯按10 秒或 1 分钟一个窗口记录,画成曲线看平稳段的均值,而不是看总平均值。

3.3 并发数:业务并发、系统并发、线程数不是一回事

这是最容易被数字游戏误导的地方。业务并发是同一时刻"正在使用系统的人";系统并发是同一时刻"正在被处理的请求数";而 JMeter 里的线程数只是施压端的虚拟用户数,它和系统并发之间差着一个"思考时间"。三者的关系可以用利特尔法则串起来,我在第 4 节详细展开。

一个常见误区是:把线程数直接等同于并发用户数。如果你在线程组里设置了 200 线程、每个请求后停顿 1 秒,而接口响应时间是 200 毫秒,那实际到达服务端的系统并发只有 200 × 0.2 / (0.2 + 1) ≈ 33 左右,服务端压力远小于你以为的 200。

3.4 错误率:分母选错,结论全错

错误率 = 失败请求数 / 总请求数。听起来没歧义,但"总请求数"的口径很有讲究。如果你的脚本里包含了登录、获取 token 这类前置准备请求,把它们算进分母,会稀释真实业务接口的错误率;反过来,如果统计周期内有大量连接超时,这些请求根本没被记录成"请求",错误率会凭空变好看。

我的做法是:用事务控制器把一次完整业务操作包成一个事务,错误率以事务为单位统计,同时单独统计超时率和连接异常率。这样得到的结论才站得住脚。

指标常见误读建议口径
响应时间只看平均值报 P50 / P95 / P99,标注样本量
TPS只看总平均值按 10s 窗口取平稳段均值
并发数线程数等于并发按利特尔法则换算系统并发
错误率分母混入准备请求以业务事务为单位统计

4. 把数字算准:利特尔法则、脉冲系数与阶梯加压

前面讲的是"怎么读",这一节讲"怎么算"。容量估算算错,后面所有测试都是在验证一个错误的前提。

4.1 利特尔法则:并发 = TPS × RT

利特尔法则是一个排队论结论:系统中的平均并发数 = 到达速率 × 平均停留时间。翻译成性能测试的语言就是:

系统并发数 = 目标TPS × 平均响应时间(秒)

假设目标是 400 TPS、平均响应时间 300 毫秒,那么系统并发就是 400 × 0.3 = 120。这个数字才是你压测时应该让服务端同时处理的请求数。而如果你在 JMeter 里加了思考时间(Think Time),线程数需要放大:

线程数 = 系统并发数 × (响应时间 + 思考时间) / 响应时间

按上例,若思考时间设为 0.5 秒,线程数 = 120 × (0.3 + 0.5) / 0.3 = 320。这个换算我每次都手算一遍,因为一旦弄错,要么压不出目标压力,要么把被测系统压穿导致结论失真。

4.2 二八原则估峰值:算完还要乘脉冲系数

很多同学估容量是拿"日活 ÷ 86400",这个数往往低得离谱。业务量的分布从来不是均匀的,二八原则更贴近现实:80% 的业务量集中在 20% 的时间内完成。

单接口峰值TPS = (日业务总量 × 80%) / (86400 × 20%)

拿日均 200 万单举例:(2000000 × 0.8) / 17280 ≈ 92.6 TPS。这个数字还是太平滑了。真实业务存在脉冲——秒杀开场、整点推送、定时任务集中触发,这时候要再乘一个脉冲系数,经验值 3 到 5 倍。上例取 4 倍,目标就是约 370 TPS,取整到 400 TPS,和我前面用的数字对上了。

这个推算链条我建议写进压测方案的第一页。它不只是一个数字,而是你和业务方对齐"测到什么程度算够"的依据。没有这个推导,评审时你说 400 TPS 达标,业务方问"为什么是 400",你答不上来。

4.3 阶梯加压找拐点:最大 TPS 与容量水位

恒定压力只能验证"这个量级能不能扛住",阶梯加压才能回答"还能扛多少"。我常用的模型是:从目标压力的 20% 开始,每 2 到 3 分钟递增 20%,一直加到错误率突破阈值或响应时间出现明显拐点为止。

拐点出现的典型特征是:TPS 不再随线程数上升,甚至开始下降,而响应时间快速拉长。这说明系统已经过了最优点,进入了排队恶化的区间。我把拐点对应的 TPS 称为最大处理能力,把目标 TPS 占最大处理能力的比例称为水位。生产环境一般建议稳定运行水位控制在 50% 到 70% 之间,留出应对突发流量的缓冲。

加压阶段时长建议观测重点
预热1~2 分钟缓存、连接池、JIT 是否充分预热
阶梯递增每级 2~3 分钟TPS 是否线性上升、RT 是否平稳
稳定保持10~30 分钟错误率、内存增长、GC 频率
峰值冲击1~3 分钟极限承载与恢复能力

4.4 施压端别自己先倒下

这一条是用血换来的。有一次我们压一个目标 2000 TPS 的接口,怎么调线程数都上不去,TPS 卡在 800 就不动了。排查了两小时,被测服务资源一切正常,最后发现是施压机自己的 CPU 跑满了,JMeter 的 GUI 模式加上实时监听器,光渲染就吃掉了大半算力。

结论很直接:压测必须用非 GUI 模式,监听器尽量精简,施压机数量按目标压力评估,必要时分布式施压。命令大致是这样:

jmeter -n -t order_peak.jmx -l result.jtl -e -o ./report --logfile jmeter.log

参数含义:-n非 GUI 模式,-t指定测试计划,-l输出结果文件,-e -o生成 HTML 报告并指定输出目录。记得每次执行前清空result.jtl,否则历史数据会混进来污染统计。

5. 指标评估:从一堆数字到一句站得住脚的结论

采集到数据只是开始,真正的活儿是评估。同样是"P95 = 800 毫秒",在不同上下文里可能是灾难,也可能是正常。评估要解决的就是这个上下文问题。

5.1 先对齐时间窗口,再看趋势

评估的第一步永远是把时间轴对齐。施压端的 RT 曲线、应用的 TPS 曲线、数据库的 QPS 曲线、主机的 CPU 曲线,如果时间基准不统一(有的用本地时间,有的用 UTC,有的采集间隔不同),你会得出完全错误的因果关系。

对齐之后看趋势,而不是看某一点的数值。我习惯把关键曲线叠在一张图上,重点观察两件事:一是同时起跳的曲线,它们大概率存在因果关系;二是先动和后动的曲线,先动的那个更可能是根因。比如 CPU 先涨、RT 后涨,那瓶颈在计算;RT 先涨、CPU 后涨,那多半是排队导致的连锁反应。

5.2 采样粒度:分钟级均值会吃掉瞬时尖峰

监控系统默认的采样间隔常常是 1 分钟,而性能问题往往在几秒内爆发。一个每秒抖动、峰值持续 3 秒的 CPU 尖峰,在分钟级均值里可能只表现为 60% 的使用率,看起来毫无问题。定位这类问题时,我会把采样粒度降到 1 秒甚至更细,并且优先看Max 值和分位数,而不是均值。

5.3 现象、根因与验证方式

下面这张表是我自己排错时常用的对照清单,遇见异常现象先按这个方向找证据,比漫无目的地翻日志效率高得多。

现象可能原因验证方式
TPS 平稳但 RT 缓慢上升内存泄漏、连接泄漏、日志堆积看堆内存曲线是否单调上升、连接池活跃数是否不回落
TPS 卡住不再上升施压端瓶颈、线程池/连接池上限看施压机资源、线程池队列长度
错误率突然爬升超时阈值触发、下游限流、连接被拒看错误码分布、下游服务日志
RT 抖动剧烈GC 停顿、锁竞争、缓存击穿看 GC 日志、慢 SQL、缓存命中率
数据库 CPU 高但 QPS 不高慢 SQL、缺索引、锁等待看慢查询日志与执行计划

5.4 基线对比:有没有基线,决定结论的可信度

单次压测的数据只能说明"这次是什么样",说明不了"是不是变差了"。真正有价值的评估是版本间对比:同一个场景、同一套数据量、同一份脚本,跑上一版和这一版,看关键指标的变化幅度。行业里常用的经验阈值是:P95 波动超过 10%、TPS 下降超过 5%,就需要给出解释。

所以我的建议是,每轮压测都要归档:脚本版本、数据量、机器规格、关键指标快照,一个都不能少。半年后有人问"这个接口是不是变慢了",你才有得比。这份归档的成本很低,但省下来的排查时间是以天计的。

6. 通过标准怎么定:分层阈值加场景准入

终于说到核心问题了。"性能测试通过标准"这件事,很多团队压根没有,全靠压测结束后大家拍脑袋。我见过的最离谱的情况是,标准写在某个人的聊天记录里。这不行,标准必须是文档化的、可量化、可复现的。

6.1 一个合格的通过标准包含四要素

范围:对哪些场景、哪些接口生效,哪些不适用。指标:用哪几个指标判定,口径是什么。阈值:每个指标的上限或下限。容错:允许的例外情况,比如冷启动前 1 分钟不计入统计。

缺了任何一个,标准都会在执行时产生争议。尤其是"容错"这一项,几乎所有人都会漏掉,结果就是每次压测报告都要为"预热阶段的异常数据算不算"讨论半小时。

6.2 分层阈值:不同层级用不同严苛度

我给一个可以直接借鉴的阈值框架,具体数字需要按你的业务调整:

层级指标参考阈值判定方式
用户体验层P95 响应时间≤ 500 ms全场景统计,排除预热 1 分钟
用户体验层P99 响应时间≤ 1500 ms全场景统计
用户体验层事务错误率≤ 0.1%以业务事务为单位
应用接口层平稳段 TPS≥ 目标 TPS取 10s 窗口均值
应用接口层线程池队列积压峰值 < 容量 80%峰值采样
资源层CPU 使用率平均 ≤ 70%,峰值 ≤ 85%排除预热段
资源层内存无持续增长趋势长稳测试 1 小时以上
稳定性GC 停顿单次 ≤ 200 ms,频率稳定长稳测试观察

这里面我个人最看重两条:内存无持续增长趋势和GC 停顿稳定。它们不写在标准里也能测,但写进去之后,团队才会真正去做长稳测试——而生产事故的相当一部分,恰恰是长稳测试才能暴露的。

6.3 稳定性与劣化标准要单独立项

功能压测达标,不代表系统能稳定跑一整天。长稳测试(通常在 2 小时到 12 小时之间,视业务周期而定)要看三件事:内存是否在 GC 后回落到基线水平、连接池活跃数是否回落到低位、错误率是否随时间漂移。单调上升的内存曲线几乎必然指向泄漏,别抱侥幸心理。

6.4 红线项和观察项分开写

标准里最好明确区分两类:红线项(不达标不允许上线,比如错误率超标、核心接口 P95 超标)和观察项(需要记录并跟踪,但不阻断上线,比如非核心接口的 P99、资源层的峰值利用率)。

这样分的好处是,评审时不需要为每个指标都争个你死我活,团队的执行成本会低很多。全都要卡死的结果往往是,标准定了没人执行,最后又回到拍脑袋。

7. 用 JMeter 跑一轮时,指标采集和判定落在哪几个点上

工具是采集器,但采集位置选错,数据就是错的。这一节讲讲用 JMeter 做一轮完整压测时,哪些配置点直接决定了指标口径。

7.1 线程组参数:不这么设就是白测

线程数、Ramp-up 时间、循环次数(或持续时间),这三个参数决定压力模型。我的习惯是:能不用循环次数就不用,改用持续时间(Scheduler),因为固定循环次数在响应时间变化时会自动改变实际压力,导致结果不可比。

参数建议设置理由
线程数按利特尔法则换算保证系统并发符合目标
Ramp-up目标线程数的 1/10 到 1/5避免瞬间冲击造成误判
持续时间至少 10 分钟让指标进入平稳段
思考时间用常数或随机定时器模拟更接近真实用户行为

7.2 监听器别乱开,用后端监听器落库

GUI 模式下开聚合报告、查看结果树,会让施压机性能暴跌。正确做法是:正式压测全部用非 GUI 模式,结果写入 jtl 文件,事后生成 HTML 报告。如果需要实时观测,用后端监听器把指标推到外部时序库,在另一台机器上看。

查看结果树在正式压测时一定要关掉,它会把每个请求的完整响应都保存下来,几百万请求能把磁盘写满,也会严重拖慢施压机。

7.3 断言与事务控制器:错误率的口径在这里定死

JMeter 默认把 HTTP 5xx 视为失败,但业务失败往往返回的是 200 + 错误码。必须在响应断言里加上业务码校验,否则错误率永远是 0。

另外,把一次完整业务操作(比如"登录 → 查商品 → 下单 → 支付")放进事务控制器并勾选"生成父样本",这个事务的耗时和成功率就是你要的核心指标。这样统计出来的错误率才是业务视角的,而不是接口视角的。

7.4 施压机与被测机资源都要采

JMeter 侧可以用 PerfMon 类的插件采集被测机的 CPU、内存、磁盘、网络,也可以直接对接外部监控系统。关键是要保证施压机本身的资源不会被自己压满——如果施压机 CPU 超过 80%,那这一轮数据基本可以作废。

7.5 报告里必须有这几样东西

我审报告时先看有没有这几项:测试环境规格与数据量、脚本与参数化数据说明、压力模型(线程数、ramp-up、持续时间)、分层指标统计表、瓶颈分析、以及结论对通过标准的逐条比对。缺了压力模型,数据无法复现;缺了环境说明,结论无法迁移;缺了逐条比对,结论就是主观判断。这三样凑齐,一份报告才算能用。

8. 几个看着达标、其实不达标的现场

最后说说我踩过或者见过的几个坑,都属于"数字很漂亮但结论是错的"这一类。

8.1 压测环境的数据量只有生产的百分之一

这是最普遍也最致命的问题。测试库只有 10 万条订单,生产有 1 亿条,同样是走索引的查询,执行计划可能完全不同——小表可能走全表扫描反而更快,大表走索引才有优势,或者反过来,小表命中缓存的概率高得多。数据量不一致的压测,只能验证逻辑正确性,不能验证性能。

我的做法是:至少让测试库的数据量达到生产的一个可接受比例(比如 10%),并且分布特征要一致——订单的时间分布、用户分布、状态分布,都得用脱敏后的生产数据构造,不能随机生成。

8.2 缓存预热之后测出的漂亮数字

压测脚本通常从第一条数据开始跑,跑几轮之后缓存全热了,指标自然好看。但生产环境是冷热混合的,而且缓存会因为扩容、重启、过期而周期性地变冷。所以我习惯在压测方案里明确:必须包含冷启动场景,以及在缓存被清空后的恢复能力测试。这一轮的数据往往比热缓存下的数据有价值得多。

8.3 只压单接口,不压混合场景

单接口压到 1000 TPS 不代表系统能撑住 1000 TPS 的混合流量。真实流量里,下单、查询、支付、退款同时存在,它们共享线程池、数据库连接、缓存资源,互相之间会有干扰。混合场景才是生产流量的正确抽象。

我的做法是按真实流量的接口占比配置吞吐量控制器,让各接口按比例施压,同时观察整体资源消耗。这样做出来的容量结论,可用性高一个档次。

8.4 忽略了"慢请求累积"

有一类问题特别隐蔽:系统在压力下不会立刻崩,而是慢请求逐渐堆积,队列越排越长,直到某个时刻突然雪崩。表现就是错误率在前 20 分钟都是 0,第 25 分钟突然跳到 30%。如果你只压 10 分钟,永远发现不了这个问题。这也是我坚持长稳测试至少跑 1 小时的原因——故障的种子往往在系统看起来最健康的时候已经埋下了。

8.5 只盯阈值,不看余量

标准写的是"CPU ≤ 70%",测试结果 68%,看起来通过。但如果这个数字是在目标 TPS 下测出来的,而生产峰值可能是目标的 1.5 倍,那 68% 对应的就是 100% 以上。所以我评估通过与否时,从来不只看"当前压力下的指标",一定会看压力曲线上的余量:从目标压力到拐点之间还有多少空间。余量不足的系统,即使当期达标,也建议在上线前做一轮扩容或优化。

实际操作中我一般会要求,在目标 TPS 的 1.3 倍压力下,核心指标仍不突破红线。这条要求救过我两次——一次是发现了缓存容量不足,一次是发现了异步任务队列在高压力下会阻塞主线程。这些在"刚好达标"的压力下都是看不到的。

提示:慢请求累积和余量不足这两类问题,只有在阶梯加压加长稳测试的组合下才会暴露。如果你的压测流程只有"恒定压力 + 10 分钟",建议至少先把时长拉长到 30 分钟,投入很小,收益很大。

我个人在实际操作中的体会是,性能测试这件事,工具熟练度只占三成,剩下七成全在口径对齐、数据构造和结论解读上。把指标定义写清楚、把通过标准文档化、把每轮数据归档好,这三件事做扎实,比换什么工具都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询