TPS、并发数、响应时间底层关系:从Little定律到性能测试实践
2026/9/7 6:47:45 网站建设 项目流程

在这个月面试了 10 个候选人之后,我只能说“会做性能测试”和“懂性能测试”之间,隔着一条很深的认知鸿沟。

绝大多数简历上写着“熟练掌握 JMeter,有性能测试实战经验”的候选人,都能熟练地告诉我:线程数设多少、Ramp-Up 怎么配、聚合报告怎么看。但当我把这个问题抛出去——TPS、并发数、响应时间,这三者的底层关系是什么?——场面往往会陷入尴尬的沉默。

有人照着面试题背答案:“TPS = 并发数 / 响应时间”。这个公式对吗?对,但不全对。它只在极理想的模型下成立。一旦系统出现排队、资源争抢、线程阻塞,这个公式会把你的测试引向一个深坑。

还有一个有意思的现象是,最近搜“jmeter 性能测试步骤”的人特别多,但很多人在第一步就错了:他们根本不确认系统真实的并发用户数是多少,直接设置线程数 500、1000 就开始压。压出来的 TPS 很漂亮,却是“虚高”的。

这篇文章不打算继续讲 JMeter 的按钮在哪,那些基础操作属于入门教程。我真正想做的,是把 TPS、并发数、响应时间三者在底层的关系彻底讲透,包括它们怎么互相影响、为什么会出现 TPS 虚高、真实业务场景中这个关系会发生什么变形,以及面试中怎么回答才不会被面试官追问到原地“爆炸”。

读完这篇文章,你至少能得到三样东西:

  1. 一套能说服面试官的、关于三者关系的底层逻辑框架。
  2. 一个判断系统真实并发承载能力的“靠谱”方法。
  3. 若干直接能用于工作场景的性能测试设计思路。

1. 这篇文章真正要解决的问题

先说一个扎心的事实:大部分性能测试报告,写出来只是为了应付上线检查,而不是为了发现系统瓶颈。

如果你只是想把压测报告做得“好看”,那不需要理解三者的底层关系——把线程数拉高,把聚合报告里的 TPS 看成一个数字,截图贴上去,完事。但如果你要解决的是以下任何一个问题,你就必须从根本上搞明白这三者的关系:

  • 线上出现卡顿或超时,但你在测试环境压不出来。测试环境怎么压都稳如老狗,一到线上就出问题。
  • 老板问“我们系统能扛多少并发”,你给不出一个可信的数字。不是不愿意给,是你根本不知道从哪个维度推导。
  • 你压测出来的 TPS 很高,但上线后一遇活动流量就挂
  • 你想做容量评估、限流配置、容量规划,但不知道用哪个指标做依据

很多人以为性能测试的核心是“会用压测工具”,其实性能测试的核心是建立指标之间的换算关系。你需要的不是一个孤立的 TPS 数字,而是“在什么并发数下、响应时间达到多少、TPS 还能不能撑住”的关联模型。

这篇文章就是围绕这个关联模型展开的:先从底层定义开始,把三个概念从“你脑子里的模糊印象”拨正为“可计算、可推导的工程指标”;再拆解三者之间在不同系统状态下会经历怎样的关系变化;接着用真实的场景案例讲清楚,为什么并发数翻倍后 TPS 反而下降;最后回到 JMeter 操作层面,告诉你一套能落地的压测设计方法。

不管你是准备性能测试面试,还是正在写压测方案,这篇文章的目标都是让你从“会用工具”提升到“能讲清楚系统表现”。

2. 先把三个概念彻底说清楚:从生活场景到技术定义

在推导关系之前,必须先确保我们聊的是同一个“并发数”。

很多人在这一步已经跑偏了。面试时我经常追问:“你的并发数是怎么定的?”答案五花八门,有说线程数的,有说注册用户数的,有说同时在线数的。这些说法都混淆了不同层面的并发概念。

2.1 并发数:是“同时发出的请求数”,不是“在线人数”

为了把这个问题说透,先讲一个生活场景。

想象一家银行网点,有 3 个柜台窗口(相当于系统有 3 个处理线程),大厅里坐着 50 个等待办理业务的客户。这时候,真正的“并发数”是多少?

不是 50。50 个客户坐在大厅里,只能叫“在线人数”或者“待处理任务数”。真正的并发数,是同一时刻正在窗口办理业务的客户数量——也就是最多 3 个。其他 47 个人,要么在排队,要么在填单子,他们没有占用柜台资源。

从技术上理解,并发数是同一时刻系统正在处理的请求数量,也就是“在途请求数”。这个“在途”,包括请求已经在操作系统网络队列里等待、已经被应用接收、正在执行 SQL、正在等待下游依赖返回等所有状态。

这个区别非常关键。你做压测的时候,把 JMeter 线程数设为 500,这 500 个只是“正在尝试发起请求的客户端”,它们大多处在等待响应阶段,并不等同于系统“同一时刻正在处理”的请求数,更不是业务层面真正的“在线用户数”。

我用一个稍微严谨一点的说法帮助记忆:

  • 并发用户数(在线用户数):业务概念,应用层有多少用户同时活跃。
  • 并发请求数(在途请求数):技术概念,系统同时处理的请求量。
  • 压测线程数(施压数):工具概念,你开了多少并发连接去发起请求。

三者有关联,但绝不是一回事。

2.2 响应时间:一个被你错误统计的指标

响应时间(Response Time, RT),字面理解是“从发起请求到收到完整响应的时间”。但在性能测试中,你需要把这条时间链路拆开看。

一次完整的请求耗时,通常包括:

  • 网络传输时间:客户端到服务器的往返时延(RTT)。
  • 排队时间:请求到达服务器后,在连接池、线程池、队列里等待被处理的时间。
  • 处理时间:应用执行业务逻辑、读写数据库、调用下游接口的时间。
  • 响应传输时间:结果返回客户端的时间。

这里有一个最常见的坑:JMeter 中显示的响应时间,默认只包含“从发出请求到收到响应”的时间,它包含了网络、排队和处理时间,但不包含你在前置处理器、后置处理器中所做操作的时间。

更关键的是,响应时间是一个分布,不是一个固定的数。同一系统在同样的压力下,1 个请求可能 50ms 返回,另 1 个请求可能 800ms 返回。所以你应该关注的是响应时间分布:

  • 平均值(Avg):容易被极端值拉偏,参考意义有限。
  • TP90 / TP95 / TP99:90%(95%、99%)的请求耗时在多少毫秒以内。这个比平均值更接近真实体验。
  • 最大值(Max):常被 GC(垃圾回收)、网络抖动之类因素拉得极高,看个趋势就好,别拿来做结论。

在后面的关系模型里,我提到的响应时间都默认指“稳定状态下的平均响应时间”。在做性能评估、容量规划时,建议你把 TP99 作为核心指标。

2.3 TPS:事务吞吐量,系统的“实际产能”

TPS(Transactions Per Second)是每秒完成的事务数。它衡量的不是系统能“接收”多少请求,而是系统能“完成”多少业务操作。

这里有个区别需要明确:TPS 和 QPS(Queries Per Second)经常被混用。严格来说:

  • QPS偏重“查询”类请求,每秒查询量。
  • TPS偏重“事务”类请求,它更强调一个完整业务操作,通常可能包含多个请求。

举个例子:你打开一次订单列表,页面会同时调 3 个接口。从 JMeter 的角度,如果你给每个接口各创建一个 Sampler,一次打开页面就是 3 个请求;如果你把整个操作封装成一个“逻辑事务”,那一次页面打开就是 1 个事务。

在做性能压测时,比较规范的做法是用“事务”作为计量单位,而不是用“请求数”。因为业务方最关心的往往是“每秒能完成多少订单”,而不是“每秒能处理多少 HTTP 请求”。如果压测报告上只写“TPS 500”,却没有说明这个 TPS 是接口级别的还是业务事务级别的,这份报告的参考价值要打一个很大的折扣。

2.4 一个简单的三句话总结

为了后文推导方便,先把三个概念的准确描述统一起来:

  • 并发数表示的是“系统同时处理的在途请求数量”,它决定了系统处于什么压力相位。
  • 响应时间表示的是“一个请求完成一次完整往返的代价”,它反映单个用户感受到的快慢。
  • TPS 表示的是“系统单位时间内真正处理完的事务总量”,它反映的是系统的产能。

一句话概括:并发数是施压的强度,响应时间是单笔处理的代价,TPS 是系统整体的产出能力。

接下来,我们就要推导这三者之间到底存在什么样的数学底层关系。

3. 三者关系的核心模型:从 Little 定律到实战公式

3.1 排队论视角下的核心公式

在性能工程的底层理论中,有一个几乎绕不开的定律——Little 定律。它最初来自排队论,后来被广泛应用在计算机系统性能分析中。

Little 定律的数学表达式很简洁:

L = λ × W

翻译成性能测试术语就是:

并发数(L) = TPS(λ) × 平均响应时间(W)

什么意思呢?它描述的是一个处于稳定状态的系统,内部同时在处理的任务数(并发数)等于单位时间新到达并被处理完成的任务数(TPS)乘以每个任务在系统内部停留的时间(响应时间)。

这个公式非常重要,因为它建立了三个指标之间的硬性换算关系。基于这个公式,可以导出两个常用形态:

TPS = 并发数 / 平均响应时间 平均响应时间 = 并发数 / TPS

3.2 为什么教科书公式在实战中经常“失效”

你会在很多面试题和培训资料里看到上面的推导公式。但如果实际压测过系统,你一定会发现一个现象:并发数从 100 提到 300,TPS 绝对不是按 3 倍线性增长

原因在于:Little 定律有一个隐含假设——系统处于稳定状态,没有过载,没有队列溢出,没有资源竞争激化。说得更直白一点,Little 定律假设系统是一个“理想管道”。但真实系统不是理想管道,真实系统有:

  • 线程池上限:处理线程被占满后,新增请求只能排队。
  • 数据库连接池上限:连接不够用,应用线程阻塞等待。
  • CPU 多核争抢:线程切换开销变大,真正干活的时间比例变小。
  • 锁竞争:多个线程同时抢一把锁,串行化的时间变长。
  • 内存 GC:并发升高后,GC 频率上升,Stop-The-World 导致响应时间恶化。

当这些瓶颈开始显现时,关系模型就变成了这样:

并发数继续上升 → 响应时间快速恶化 → TPS 不再上升,甚至下降

这个拐点就是性能测试中最重要的发现:系统的最佳并发区间在哪里

为了让你看清楚这个变化过程,我用一张关系演变表来描述系统从空闲到被打垮的过程:

阶段并发数变化响应时间TPS 变化系统状态
空闲期低并发稳定且小随并发线性上升资源空闲,产能未被充分使用
健康期并发提升轻微上升,总体平稳随并发近似线性增长资源利用率逐渐提升
饱和点到达拐点开始明显上升增长趋缓,达到峰值某个资源开始打满
过载区继续加压急剧上升不增反降排队严重,资源耗尽
崩坏区极端高并发大量超时大幅下跌线程池拒绝、连接池满、雪崩风险

这五个阶段至关重要。所谓“懂性能测试”,核心就是能识别出你的系统当前处于哪个阶段,以及拐点在哪里。

3.3 用一个具体场景验证公式

假设有一个系统,压测场景如下:

  • 平均响应时间 = 200ms(0.2 秒)
  • 并发数 = 50
  • 按公式计算:TPS = 50 / 0.2 = 250

这说明:如果每个请求耗时 0.2 秒,系统同时能处理 50 个请求,那 1 秒内能处理完 250 个请求。

现在把并发数提升到 100,会出现什么情况?

  • 理想情况:如果系统资源充足、没有竞争,响应时间仍为 0.2 秒,则 TPS = 100 / 0.2 = 500,翻倍。
  • 常见情况:并发提升后,线程切换、锁竞争、数据库连接等待一起出现,平均响应时间可能涨到 0.35 秒,那么 TPS = 100 / 0.35 ≈ 286。虽然并发翻倍了,TPS 只多了 14%。
  • 过载情况:并发数到 200 以后,响应时间涨到 1.2 秒,TPS = 200 / 1.2 ≈ 167,不但没涨,反而比并发 100 时更低。

这就是为什么很多人在压测时会看到“TPS 虚高”或“TPS 暴跌”的现象——因为测试没有建立“由响应时间推导 TPS”的意识,只是机械地增加线程数,看着聚合报告里的数字上蹿下跳,却说不出系统到底哪里出了问题。

4. 真实系统里,三者关系会发生哪些变形

4.1 不同接口类型下的关系差异

很多刚接触性能测试的人会犯一个错误:把所有接口都用同一套并发-响应时间模型来理解。实际上,不同接口类型下,三者的关系表现差异巨大。

先看“快接口”。典型的例子是查询类的接口,比如登录校验、缓存查询、状态检查。这类接口响应时间很短,通常在 5ms 到 50ms 之间。在低并发阶段,它们能支撑非常高的 TPS,因为每个请求在系统内逗留时间极短。但这类接口一旦并发打高,最先出现问题的是网络层和线程池,响应时间可能从 10ms 暴涨到 500ms。

再看“慢业务”。典型例子是报表导出、批量任务、大文件上传、涉及大量复杂 SQL 的查询。这类接口响应时间可能在 1 秒甚至更长。重点在于:慢接口是 TPS 的天敌。同样是 100 个并发请求,快接口能轻松做到 2000+ TPS,慢业务可能只能做到 50 TPS。不是因为系统能力差异,而是因为每个请求占据处理线程的时间太长。

这种差异在容量规划时的含义完全不同。压测一个查询接口时,你可以用“并发数 500、TPS 3000”来规划;但压测一个导出报表接口时,同样的并发数 500 可能直接把系统打挂,因为每个请求处理时间太长,线程池很快就耗尽了。

4.2 有状态系统与无状态系统的区别

另外一个影响三者关系的因素是系统的“状态性”。

无状态应用:比如纯 Redis 查询、静态资源服务、计算型接口,不依赖会话数据、不持有用户状态。它的并发模型相对简单,Little 定律的推导会比较贴近现实。资源充足时,并发和 TPS 基本线性关系。

有状态应用:比如在线交易、购物车、WebSocket 长连接、分布式锁、乐观锁更新等。此时系统不仅要处理计算,还要处理“状态同步”。并发升高时,数据一致性、锁冲突、脏读重试带来的额外开销会急剧增加,响应时间会非线性上升。

从压测角度看,有状态系统的性能测试,绝不能用无状态系统的“线性模型”去预测结果。你必须通过阶梯加压的方式,找到真实的饱和点。

4.3 异步化对三者的关系颠覆

还有一个不可忽视的变量:异步化。

如果系统大量使用消息队列、异步回调、线程池异步处理,用户的“请求-响应”链路和“业务处理链路”就解耦了。这种情况下会出现什么?

  • 对用户来说:响应时间变短了,因为请求只是被接收后放进了队列,立即返回“受理成功”。
  • 对系统来说:真正的业务处理是在后台异步进行的,TPS 的计量口径变了。
  • 对压测来说:你看到的响应时间是“受理时间”,而不是“业务完成时间”。

此时仍套用“TPS = 并发数 / 响应时间”就会得出严重失真的结论。一个系统中如果有大量异步处理,你必须分别观察“入口 TPS”和“处理 TPS”,以及两者之间的队列积压量。队列积压量才是观察系统压力的关键指标,而不是直接看响应时间。

所以,在回答三者关系之前,先要反问:当前系统是同步的还是异步的?如果是异步系统,三者的关系链条要重新梳理。

5. 为什么你会看到“TPS 虚高”?——并发数、响应时间与统计口径的三角关系

“TPS 虚高”是我们搜索热词中出现频率非常高的一个词。这个词概括了性能测试实践中一类常见的错误认知:压测报告上的 TPS 很高,但真实性存疑。

5.1 虚高 TPS 是怎么产生的

先说结论:TPS 虚高的本质,是统计口径和真实业务模型不匹配,而不是压测工具出了问题。

常见产生虚高 TPS 的操作有:

第一种:只用低响应时间接口做压测。如果一个系统只有一个查询接口,响应时间 10ms,那 500 并发就能压出 20000+ TPS。但这能代表系统整体能力吗?明显不能。业务高峰期的流量是混合的,包含慢接口、写接口、长事务。压测场景设计时如果只挑快接口打,得到的 TPS 没有任何参考价值。

第二种:忽略事务定义,直接把 HTTP 请求数当 TPS。这在 JMeter 中特别常见。默认情况下,JMeter 聚合报告里的 Throughput 是按照 Sampler 的执行次数来统计的。一个页面包含 10 个接口,用户一次完整操作会产生 10 个请求。如果你把这个数字直接当成业务 TPS,报告上的数字会比真实业务吞吐量高出 10 倍。

第三种:高并发长时间压测,取峰值 TPS 当结果。很多压测报告习惯取“最高那一段的 TPS”作为成果。但是这个最高值通常出现在系统资源尚未稳定、缓存未预热、垃圾回收尚未开始的时候。你真正应该关注的是系统在“持续压力下”的稳定 TPS,而不是瞬时峰值。

第四种:忽略错误率。如果系统大量返回 500、超时、连接拒绝,JMeter 会统计这些“失败的请求”吗?默认情况下,JMeter 不会将断言失败或者 HTTP 非 2xx 状态码算作成功事务,但如果你没有配置断言,或者把错误响应也当作“完成”来统计,你的 TPS 就会把失败请求也计算进去,看起来很高,实际上是假象。

5.2 怎么避免 TPS 虚高

从工程经验来看,避免 TPS 虚高的核心有四点:

  • 压测场景必须包含核心链路,而不是只挑快接口
  • 事务定义必须和业务操作对齐。打开订单列表 = 1 个事务,而不是 3 个 Sampler。
  • 报告 TPS 必须关联错误率和响应时间分布。TPS 再高,如果 TP99 已经突破 2 秒,也不算健康。
  • 结果必须取稳定段平均值,而不是瞬时峰值

如果你在压测报告中写“TPS 峰值 5000”,面试官或者老板追问一句“响应时间多少?错误率多少?并发多少?什么场景?压了多久?”你能清晰回答,才算真正对这个数字负责。

6. 通过压测实验反向验证三者关系:JMeter 阶梯加压实操

理论讲完了,现在进入实操环节。既然你是做性能测试的,JMeter 是绕不开的工具。这一节我会给你一套可以直接执行的压测设计思路,用 JMeter 来验证“并发数、响应时间、TPS”三者之间的动态关系。

6.1 环境准备

本小节以最常用的 JMeter 为例。当前 JMeter 的主流版本为 5.x,需要 JDK 8 或以上版本,建议使用 JDK 11 或 17。不同 JMeter 版本的界面细节会有差异,但核心操作思路一致。如果还没有安装 JMeter,可以去 Apache JMeter 官网下载解压版,解压后进入bin目录,Windows 双击jmeter.bat,macOS/Linux 执行./jmeter即可。

为了方便演示,准备一个简单的被测服务。你可以使用任意本地 Web 服务,这里用一个最小的 Spring Boot 项目,提供一个模拟业务处理接口:

// 文件路径:src/main/java/com/example/perfdemo/controller/DemoController.java package com.example.perfdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController public class DemoController { /** * 模拟耗时业务接口。 * delayMillis 参数用于模拟业务处理耗时,默认 100 毫秒。 */ @GetMapping("/demo") public String demo(@RequestParam(value = "delayMillis", defaultValue = "100") long delayMillis) throws InterruptedException { // 模拟业务处理耗时,实际项目中这里是 SQL 查询、下游调用、复杂计算等 TimeUnit.MILLISECONDS.sleep(delayMillis); return "success"; } }

这个接口很简单:每个请求进来,模拟处理 100ms,然后返回。为什么设计这个接口?因为你只有让每个请求固定占用一定时间,才能清晰观察到并发数增加后,线程池、CPU、响应时间和 TPS 之间如何互相制约。

如果不想写代码启动服务,也可以使用任一 Linux 环境,用 Python 开启一个临时 HTTP 服务,再通过time.sleep模拟耗时。核心目标只是“有一个可被压测的接口”。

6.2 JMeter 测试计划设计

打开 JMeter,按下面的步骤建一个最小测试计划:

  1. 右键“测试计划”,添加一个“线程组”。
  2. 在线程组下,添加“Sampler -> HTTP 请求”。
  3. 在 HTTP 请求中配置:
    • 协议:http
    • 服务器名称或 IP:localhost
    • 端口号:8080
    • 方法:GET
    • 路径:/demo?delayMillis=100
  4. 在线程组下,添加“监听器 -> 聚合报告”和“监听器 -> 响应时间图”。

线程组配置这里先不固定,因为我们要做阶梯加压。阶梯加压的核心思路是:同样的测试脚本,从低并发开始,逐步增加线程数,每一档保持运行一段时间,观察各项指标的变化趋势。

比如按下面规模来:

阶段线程数Ramp-Up 时间持续时间
阶段1101 秒30 秒
阶段2201 秒30 秒
阶段3502 秒30 秒
阶段41005 秒60 秒
阶段520010 秒60 秒
阶段640020 秒60 秒

为什么要设置“持续时间”?因为性能测试必须看稳定状态下的表现。启动后立即出现的数字可能受线程初始化、缓存预热影响。一般建议:每个阶段的持续时间不少于 30 秒,最好 60 秒以上,让数据趋于稳定后再记录。

如果你不想手动创建 6 个线程组,也可以使用 JMeter 的插件Ultimate Thread Group(需要在 JMeter Plugins Manager 中安装),它可以在一张图表里配置多个阶梯阶段的线程数。核心思想不变:阶梯加压、逐级递增。

6.3 使用命令行执行压测

JMeter 官方建议在压测时使用命令行模式而不是 GUI 模式,因为 GUI 模式本身会占用系统资源,影响压测结果的准确性。按下面的命令执行:

jmeter -n -t perf-test.jmx -l result.jtl -e -o report

参数说明:

  • -n:非 GUI 模式运行。
  • -t:指定测试计划文件路径。
  • -l:输出原始结果文件,JTL 格式。
  • -e:测试结束后生成 HTML 报告。
  • -o:HTML 报告输出目录,目录必须不存在或为空。

执行完成后,打开report目录下的index.html,即可看到完整的图表化报告,包括 TPS、响应时间分布、错误率等信息。

6.4 预期结果与数据解读

在我设计的 Demo 接口下,预期你会看到以下现象:

  • 低并发阶段(10、20):平均响应时间接近 100ms 左右,TPS 随并发数近似线性增长。
  • 中等并发阶段(50、100):响应时间可能小幅上升到 120ms 左右,TPS 仍在增长,但增速放缓。
  • 较高并发阶段(200、400):响应时间明显上升,可能出现 300ms 甚至 500ms 以上的值。TPS 要么增长极其缓慢,要么开始下降。

为什么在这个 Demo 中并发 200 就出现明显拐点?因为 Spring Boot 默认内嵌 Tomcat 的max-threads默认值是 200。当并发请求数超过 200 时,多余的请求会进入 Tomcat 的accept-queue排队。这样一来,请求的“等待时间”开始大幅增加,响应时间快速上升,TPS 增速放缓甚至下降。

这个实验清晰地展示了一个道理:系统的最大并发承载能力,取决于系统内部最窄的资源的处理能力。对 Demo 服务来说,最窄的资源就是 Tomcat 的最大工作线程数。每增加一个线程都在增加处理能力,但一旦线程池满了,加并发只会让队列越来越长,TPS 不再增长。

6.5 通过一个最小 Python 脚本模拟并发与 TPS 的关系

如果你希望在代码层面更直观地理解“并发数、响应时间、TPS”三者之间的换算,可以用一个最小 Python 脚本模拟这个过程。

# 文件路径:simulate_concurrency.py import time import threading import requests from concurrent.futures import ThreadPoolExecutor BASE_URL = "http://localhost:8080/demo?delayMillis=100" def send_request(_): start = time.time() try: requests.get(BASE_URL, timeout=5) except Exception as e: print(f"请求异常: {e}") return time.time() - start def run_test(concurrency, total_requests=200): start = time.time() with ThreadPoolExecutor(max_workers=concurrency) as executor: latencies = list(executor.map(send_request, range(total_requests))) total_time = time.time() - start avg_rt = sum(latencies) / len(latencies) tps = total_requests / total_time print(f"并发数={concurrency}, 总请求数={total_requests}, 总耗时={total_time:.2f}s, " f"平均响应时间={avg_rt * 1000:.1f}ms, TPS={tps:.1f}") return tps, avg_rt if __name__ == "__main__": for concurrency in [10, 20, 50, 100, 200, 400]: run_test(concurrency)

运行方式:

python simulate_concurrency.py

需要注意:这个脚本的并发模型是有限线程池,它模拟的“并发数”是从客户端视角发起的并发连接数,和服务器端实际并发处理数是两码事。它只能帮你从宏观上理解趋势,不能取代 JMeter 这类专业压测工具。

如果通过这个脚本观察输出的 TPS,你会发现一个现象:在低并发时,TPS 粗略满足TPS ≈ 并发数 / 平均响应时间;在并发数超过系统处理能力后,平均响应时间上涨,TPS 不再同步上涨。

7. 常见问题与排查思路:并发数、TPS、响应时间“对不上”怎么办

在实际项目中,你会发现压测出来的数据和公式推导的对不上,甚至同一套系统,隔一天压出来的数据都不一样。下面整理了几个高频问题。

问题现象可能原因排查方式解决方案
并发数增加,TPS 却几乎不变系统存在单点瓶颈,如数据库连接池、单线程处理逻辑、锁竞争查看数据库连接池使用率、CPU 使用率、线程阻塞情况加大连接池上限;优化锁粒度;拆分热点资源
并发数增加,响应时间暴涨,TPS 下降系统已经过载,线程池满,请求在排队查看 Tomcat 线程池活跃线程数、队列长度降低并发压力,先找拐点;优化处理耗时;引入限流保护
TPS 很高,但错误率同步上升断言未配置完整,失败请求被计入成功;或服务端大量返回错误查看 JTL 日志中的错误码;添加响应断言;核对压测结果统计口径在 JMeter 中增加断言;过滤错误响应后再统计 TPS
压测响应时间正常,但线上响应时间很慢压测数据不真实:只打了缓存命中场景,未覆盖慢查询;线上数据量和并发模型更复杂检查压测数据是否与线上数据特征一致;检查慢查询日志使用贴近生产的数据量;增加混合场景
同一套系统,两次压测结果差异极大压测机自身资源不足,或 JVM 垃圾回收产生抖动监控压测机 CPU、内存使用率;查看服务端 GC 日志压测机单独部署,避免资源争抢;压测前预热系统
偶尔出现个别请求响应时间极高网络抖动、GC 停顿、线程池满的瞬间排队查看 TP99、TP999 而不是只关注平均响应时间分析 GC 日志和网络监控;调整 JVM 参数
JMeter 聚合报告中的 Throughput 和手算 TPS 不一致事务定义不一致;JMeter 默认按请求统计,未按事务统计检查是否是“逻辑事务组”模式;确认每个事务包含几个 Sampler使用“事务控制器”包裹完整业务流程;统一 TPS 统计口径

值得强调的一点是:在排查性能问题时,不要只盯着压测工具的输出。TPS、响应时间、并发数只是“现象”,底层原因往往在系统资源层:CPU 使用率、线程池活跃数、数据库连接池活跃数、GC 频率、网络带宽、磁盘 IO。任何一层的资源耗尽,都会表现为“并发数上去了、TPS 上不去”。

8. 性能测试设计与工程最佳实践

到这里,你已经理解了并发数、响应时间、TPS 之间的底层关系。接下来需要把这套认知沉淀为工程实践,否则理论永远是理论。

8.1 从业务流量反推压测负载

很多人在设计压测场景时,凭感觉设置线程数。正确的做法应该是从业务流量反推。

假设你的业务在高峰时段的日活用户是 10 万,核心接口的 QPS 预估是 2000,平均响应时间要求小于 500ms,那压测配置应该这样推导:

  • 根据 Little 定律,需要并发数 = QPS × 平均响应时间 = 2000 × 0.5 = 1000。
  • 但是 1000 是“业务请求并发”,不是“JMeter 线程数”。因为 JMeter 一个线程可以连续发起多个请求,实际线程数还需要考虑单线程的循环次数和思考时间(Think Time)。

如果你的压测脚本没有设置思考时间,1000 个并发线程会不间断地发起请求,这属于“极端压力”,不是“真实压力”。真实用户的操作是有思考间隔的。所以压测时通常分两种模式:

  • 不上思考时间:探求系统最大处理能力,找到性能拐点。
  • 加上思考时间:模拟真实用户行为,评估线上容量。

两种模式的目标不同,不能混为一谈。

8.2 阶梯加压是常规操作

不要一上来就直接压 1000 并发。更稳的做法是:先小并发(例如 10)跑一遍,确认功能正确、无报错;然后按 1-2-5-10-20-50 的倍数关系递增;每个梯队保持至少 30 秒;记录每个梯队下 TPS、响应时间、错误率三个指标。

这种做法的好处是,你不仅能知道系统“挂了”,还能找到“从哪个并发开始变慢”。这个拐点比“压崩了”这个结果有价值得多。

8.3 场景设计要覆盖混合链路

性能压测不应该只压一个“最慢接口”或“最快接口”。真实用户的一个操作往往调用多个接口,包含读缓存、读数据库、写数据库、调用下游等不同行为。

比较合理的场景设计是:按照生产环境的流量占比,设计不同接口的混合比例。举例来说:

  • 查询订单接口占比 60%
  • 创建订单接口占比 20%
  • 取消订单接口占比 10%
  • 支付回调接口占比 10%

这种混合场景压出来的结果,才接近线上真实情况。

8.4 正则提炼:并发数设置与确认

针对热搜词“jmeter 压测怎么确认系统的并发数”,这里给出一个可执行的确认路径:

  1. 第一步:从业务方获取核心接口的高峰 QPS 数据和平均响应时间要求。
  2. 第二步:用 Little 定律计算理论并发数:并发数 = QPS × 平均响应时间
  3. 第三步:在 JMeter 中用该并发数初步执行压测,观察实际响应时间是否达标。
  4. 第四步:逐步增加并发数,直到响应时间不再满足要求,此时记录的并发数就是系统当前的“极限并发数”。
  5. 第五步:加上安全余量(通常取极限并发数的 70% 左右)作为生产环境的限流阈值或容量规划基线。

注意:如果系统使用异步模型、消息队列等架构,这个推导过程还需要额外引入队列积压量的监控指标,而不是只盯响应时间。

8.5 关注稳定性测试与异常场景

除了短时间压测找拐点,还需要做稳定性压测。常见做法是:以 70% 到 80% 的极限并发数持续压测 30 分钟到 1 小时,观察是否存在内存泄漏、连接池泄漏、线程数持续增长、GC 时间不断变长等问题。

稳定性压测最容易暴露的问题包括:

  • 内存泄漏导致 Full GC 频繁,响应时间周期性恶化。
  • 数据库连接池泄漏,连接被占满。
  • 日志框架导致 IO 阻塞。
  • 定时任务与峰值流量叠加导致资源竞争。

8.6 安全提醒:别在生产环境乱压

压测是会对系统产生高负载的行为,任何时候都不要未经授权直接对生产环境发起压力测试。正确的做法是:

  • 先在测试环境验证压测脚本和预期结果。
  • 如果确实需要进行生产环境的容量验证,需要提前协调好运维、研发、DBA 等相关人员,选定低峰期,并在有监控告警和回滚预案的情况下进行。
  • 生产压测前必须确认限流降级策略已经就绪,避免压测导致雪崩。

一句话:压测不是“大家都在测”就可以随便做的事,授权、备份、回滚、监控缺一不可。

9. 回到面试题:怎么回答 TPS、并发数、响应时间的底层关系

把前面的内容理清之后,现在可以回答标题里那个问题了。

如果在面试中我被问到“TPS、并发数、响应时间三者之间存在什么样的底层关系”,一个能让面试官眼前一亮的回答思路是:

这三者之间的关系不能用一个孤立的公式来概括,而应该用系统在不同负载阶段的行为变化来描述。

在系统还没达到资源瓶颈时,它们基本满足 Little 定律:并发数 = TPS × 平均响应时间。也就是说,TPS 和平均响应时间的乘积,必须等于系统的在途并发数。这是排队论中一个守恒关系。

但一旦系统进入过载区,这个公式就会“变形”。因为响应时间不再稳定,而是随着并发数上升而快速恶化,此时 TPS 会被响应时间拖住,甚至下降。所以更准确的表述是:并发数是输入,它影响响应时间和 TPS;响应时间是被影响项,它反映了系统压力;TPS 是输出,它体现系统的真实产能。核心的工程能力在于找到“系统从线性增长阶段进入饱和阶段的拐点”,这个拐点才是容量评估的关键。

接着,主动给面试官讲一个自己实际做过的压测场景:“我之前压测过一个订单查询接口,在并发 100 时 TPS 能到 1200,响应时间 80ms;并发拉到 300 后,响应时间涨到 250ms,TPS 反而降到了 1100。后来定位到是数据库连接池不够用,连接等待时间大量增加。调整连接池参数后,同样的并发下,TPS 重新回到了 1800 左右。”

这段回答的好处在于:

  1. 展现了你懂理论公式(Little 定律)。
  2. 展现了你知道公式的边界(过载区会失效)。
  3. 展现了你做过真实测试(能说出具体数字变化)。
  4. 展现了你有排查问题的能力(定位到连接池)。

这个回答的逻辑链条,比背诵“TPS = 并发数 / 响应时间”要完整得多。

10. 总结:从“会压”到“懂性能”

回到最初的话题:会做性能测试的人很多,但能讲清楚 TPS、并发数、响应时间底层关系的人,才是真正能解决线上疑难杂症的人。

这篇文章从头到尾,其实就围绕一条主线:并发数是施加给系统的压力,响应时间是系统感受到的代价,TPS 是系统交出的产能。三者的关系从理想状态的 Little 定律出发,在真实系统中会因为线程池、连接池、锁竞争、GC、异步化等因素发生变形。理解这些变形,找到系统的拐点,才是性能测试的核心价值。

如果你正在准备性能测试面试,不要只背公式。你可以按照这篇文章第 6 节的思路,自己搭一个 Demo 服务,用 JMeter 跑一遍阶梯加压,亲手画出 TPS 和响应时间随并发数变化的曲线。当你亲眼看到并发 50 时 TPS 稳定增长、并发 200 时响应时间开始“起飞”、并发 400 时错误率飙升——你对三者的理解,就不再是文字层面,而是刻进直觉里的东西。

下一步你可以继续深入学习:JMeter 的分布式压测配置、全链路压测方案、容器化环境下的性能测试、数据库慢查询分析与索引优化、JVM 调优与 GC 日志分析。这些都是性能测试进阶路上绕不开的主题。

如果这篇文章对你有帮助,建议收藏备用。下次做压测时,把本文第 8 节的“并发数确认路径”拿出来对着做一遍,你会发现性能测试报告的质量会有明显提升。

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

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

立即咨询