一句话先说清:这三个词说的根本不是一回事——「在线」是餐厅里坐着的人,「并发」是同一秒真的在下单的人,「虚拟用户」是你雇来按剧本反复下单的试吃员;把它们当成一个数,压测的压力会配错好几倍,报告结论也会跟着写错。
一、是什么
1. 先用一个餐厅类比
| 餐厅里的角色 | 对应概念 | 它在做什么 | 会不会给厨房派活 |
|---|---|---|---|
| 店里坐着的所有客人:看菜单的、聊天的、发呆的 | 在线用户数 | 占着座位 | 不一定 |
| 同一秒把点单递给服务员的客人 | 并发用户数 | 正在给厨房下单,且在等菜 | 会 |
| 你雇来按剧本一遍遍点单的试吃员 | 虚拟用户(VU) | 按写好的脚本重复动作 | 会,但他是程序,不是真人 |
一个关键直觉:5000 个客人坐在店里,厨房同一秒真正要做的菜可能只有 10 盘。座位(会话)的数量、下单的频率、同时在做几盘菜,是三个完全不同的数。 压测工具上的旋钮(配多少个 VU)只是第三个数的「生成装置」,既不等于在线人数,也不等于并发请求数。
2. 三个定义
在线用户数(Online Users) :登录状态还没失效的人数。只要用户没退出、会话(session,登录后服务端用来记住「你是谁、进行到哪一步」的那份记录)没过期,哪怕他正盯着屏幕发呆,也算在线。
- 它衡量的是存量(某个时刻「挂着多少个人」),单位是人。
- 它主要消耗服务端「占着不放」的资源:会话内存、长连接、连接数上限。
- 它不直接产生 CPU 计算压力——一个发呆的用户不给系统派活。
并发用户数(Concurrent Users) :在某一个瞬间,已经把请求发出去、还没收到响应的用户数量。它是一个快照,不是一段时间里累计的人数。这里有两个容易混的口径,必须说清用哪个:
| 口径 | 数的是什么 | 什么时候用 |
|---|---|---|
| 业务口径 | 同一时刻正在操作系统的人(含刚点完按钮、正在等结果的人) | 跟业务方沟通、写需求 |
| 服务端口径 | 同一时刻系统里正在处理的请求个数(在途请求数) | 看服务端线程池、连接池、排队情况 |
一个用户点一次按钮,前端可能拆成 3 个请求,所以两个口径能差好几倍。今天统一按「同时有请求在路上(在途) 」来理解,因为只有这个数能跟系统资源对上。
虚拟用户(VU,Virtual User)/ 线程(Thread) :压测工具里模拟一个用户行为的一段循环程序。JMeter 里叫线程,k6 里叫 VU,本质是同一样东西:一段脚本反复执行「发请求 → 等响应 → 按设定等待一会儿 → 再发」。
- 它是工具参数,不是业务指标,也不是系统指标。
- 在同步脚本(一轮只发一个请求、发完等响应)里,1 个 VU 同一时刻最多只有 1 个请求在途。这是理解后面所有换算的钥匙。
- 一个 VU 每秒能发多少次,取决于「一轮耗时」= 响应时间 + 脚本里的等待时间(思考时间)。同样是 10 个 VU,脚本里等 0 秒和等 1 秒,产生的压力能差几十倍。
3. 四个量的量纲
| 量 | 大白话 | 单位 | 谁决定它 | 常见错误 |
|---|---|---|---|---|
| 在线用户数 N | 挂着多少人 | 人 | 业务、会话超时时间 | 直接拿去配线程数 |
| 操作间隔 t | 每人多久点一次 | 秒/次 | 真人行为、业务节奏 | 拍脑袋默认 0(等于真人每秒点一次) |
| 到达率(每秒请求数) | 每秒有多少活送到系统 | 次/秒 | N ÷ t | 把它叫成「并发数」 |
| 在途请求数(并发) | 同一瞬间系统手里攥着几个请求 | 个 | 到达率 × 响应时间 | 把它当成要配的 VU 数 |
| 虚拟用户数 | 工具上要开几段脚本 | 个 | 到达率 × 一轮耗时 | 写进报告当成业务结论 |
它们之间不是并列的,而是一条链:
业务侧:在线人数 N + 操作间隔 t
→ 每秒到达多少请求(目标的真实量纲)
→ 每个请求在系统里停留多久(响应时间 RT)
→ 同一瞬间有多少请求在途(并发,服务端口径)
→ 要开多少个 VU 才能造出这个到达率(工具旋钮)
把这条链上的任意两环画等号,就是压测里最常见的一类错误。
二、为什么
不掌握它的三个真实后果
后果一:压力配错数量级,压测机先崩。 业务说「5000 人在线」,直接配 5000 个线程。真人有思考时间(看页面、填表单),5000 人在线每秒可能只发 500 个请求;而 5000 个不知疲倦的线程会疯狂连发,先在施压端把 CPU、内存、端口吃满。这时测出来的是「压测机的极限」,不是「被测系统的极限」。
后果二:报告结论没有业务含义。 报告里写「系统在 100 并发下 P95 = 8.6 秒」,业务方问「那到底能撑多少人在线?」,答不上来。因为「100」是工具旋钮的刻度,不是业务量;要回答业务问题,得补上「每人多久操作一次」这个翻译参数。
后果三:不同脚本、不同工具的结果不可比。 同一个接口、同一个响应时间,A 脚本一个 VU 每秒发 48 次,B 脚本因为加了 1 秒等待每秒只发 0.98 次,差约 49 倍。如果只看「都用了 50 VU」就认为两次压测等价,趋势对比、版本基线、容量结论全部作废。KP-005 讲吞吐量口径,KP-007 讲思考时间与步调,都是这条链的延伸。
三、怎么用:四步翻译法
第 1 步:向业务要三个数(不要只问「多少人」)
| 要问的 | 为什么必须问 | 拿不到时怎么办 |
|---|---|---|
| 峰值在线人数 N | 决定存量规模 | 从会话数 / 网关活跃连接统计取 |
| 每人平均操作间隔 t(秒) | 决定到达率,是把「人」翻成「次/秒」的唯一桥梁 | 从访问日志算:总请求数 ÷ 活跃用户数 ÷ 统计时长 |
| 峰值系数(平时 vs 高峰倍数) | 避免拿日均值去压峰值 | 取高峰时段与全天均值的比值 |
第 2 步:算目标到达率
目标每秒请求数 = 在线人数 N ÷ 每人操作间隔 t
例如 5000 人、每人 10 秒点一次 → 500 次/秒。这个 500 既不是并发数,也不是 VU 数,它是「每秒有多少活送到系统」。
第 3 步:定脚本节奏,算单个 VU 的产出
一轮耗时 = 响应时间 RT + 思考时间(脚本里主动等待的秒数)
单个 VU 每秒发出次数 = 1 ÷ (RT + 思考时间)
| 脚本节奏 | 一轮耗时 | 单 VU 产出 | 适用场景 |
|---|---|---|---|
| 无等待(闭眼连发) | ≈ RT | 很高 | 找系统极限、压测机能力校验;不代表真人 |
| 有思考时间 | RT + t | 接近真人频率 | 负载测试、容量验证、浸泡测试 |
| 固定步调(按目标速率发) | 由步调控制 | 精确可控 | 需要稳定到达率的拐点测试 |
判据:报告里写「用了 N 个 VU」时,必须同时写清一轮耗时或思考时间,否则这个数字不可复现。
第 4 步:反推 VU 数,并用并发量交叉校验
所需 VU 数 = 目标每秒请求数 ÷ 单个 VU 每秒发出次数
并发在途请求数 ≈ 目标每秒请求数 × 响应时间
两个校验:
- 若算出的 VU 数远小于目标「并发用户数」,不要慌——因为在途请求数通常远小于在线人数,这恰恰是对的;
- 若算出的 VU 数超过压测机可承载的线程 / 协程上限,先优化脚本或分布式施压,不要硬开。
决策速查表
| 目标 | 该用哪个量 | 看什么指标 | 通过判据 |
|---|---|---|---|
| 回答「能撑多少人在线」 | 在线人数 + 操作间隔 → 到达率 | 目标到达率下 P95、错误率、资源利用率 | 到达率达标且 P95 ≤ SLA、错误率达标 |
| 配置压测脚本 | VU 数(由到达率反推) | 实际每秒请求数 | 实测到达率 ≈ 目标到达率(±5%) |
| 判断服务端是否排队 | 在途请求数 | 线程池队列、RT、每秒完成数 | RT 未随到达率同步暴涨 |
| 写报告结论 | 到达率 + 对应在线人数 | 二者成对出现 | 不出现孤立的「XX 并发」 |
四、举个例子:完整走一遍,每个数你都能自己算
场景:登录接口,业务方说「高峰期有 5000 人在线,帮我测一下扛不扛得住」。
第 0 步:先看一组数据
1 / 20 / 50 / 100 线程四组梯度结果(各跑 60 秒):
| 线程数 | 样本数 | 平均 RT (MS) | P95 (MS) | 每秒完成数 | 错误率 |
|---|---|---|---|---|---|
| 1 | 2855 | 20.71 | 25 | 47.70 | 0% |
| 20 | 1224 | 946.63 | 1804 | 20.45 | 0% |
| 50 | 1319 | 2120.68 | 3256 | 22.06 | 0% |
| 100 | 906 | 5836.27 | 8642 | 15.14 | 0% |
(这里的「每秒完成数」是整窗口平均值:样本数 ÷ 统计窗口时长。它和「瞬时每秒完成数」不是一回事。)
先自己算一遍倍数:
- 线程:1 → 100,×100
- 平均 RT:20.71 → 5836.27 ms,×281.8
- 每秒完成数:47.70 → 15.14,×0.32
线程加了 100 倍,系统每秒干完的活只剩三分之一。
因果链是这样的:线程变多 → 请求同时涌进来 → 服务端线程池 / 连接池排队 → 每个请求在队列里等的时间被算进响应时间,RT 从 20ms 涨到 5.8 秒 → 每个线程要等 5.8 秒才能发下一个请求,单线程产出从 48 次/秒掉到 0.17 次/秒 → 虽然线程数 ×100,但单线程产出掉了约 280 倍 → 总吞吐不升反降。
一句话:线程数不是压力,压力是「每秒送到系统多少活」,而 RT 会反过来限制每个线程能送多快。 这四组是分四次独立跑出来的(不是一次连续加压),每档重新冷启动,所以跨档只能看趋势、不能当精确曲线;真正的阶梯加压要在一个进程里连续加。
第 1 步:把「5000 在线」翻译成每秒请求数
假设每人平均 10 秒点一次(这个数必须问业务或从日志取,不能拍脑袋):
5000 人 ÷ 10 秒/次 = 500 次/秒
500 是到达率,不是并发用户数,更不是 VU 数。 这是最容易混淆的一步。
第 2 步:算「1 个 VU 每秒能发几次」
用 1 线程档实测的 RT = 20.71 ms = 0.02071 秒,分两种脚本节奏:
| 脚本节奏 | 一轮耗时 | 单 VU 每秒发出次数 | 验算 |
|---|---|---|---|
| A:发完立刻发下一个(无等待) | 0.02071 秒 | 1 ÷ 0.02071 ≈ 48.29 次/秒 | 与实测 47.70 基本吻合,差值来自启动预热与统计窗口 |
| B:每轮加 1 秒思考时间(贴近真人) | 0.02071 + 1 = 1.02071 秒 | 1 ÷ 1.02071 ≈ 0.98 次/秒 | 约等于「每人每秒点一次」 |
同一个接口、同一个响应时间,只因为脚本里多了一句等待,单个 VU 的产出差了 48.29 ÷ 0.98 ≈ 49 倍。
第 3 步:反推需要多少 VU
| 脚本节奏 | 单 VU 产出 | 目标 500 次/秒需要 | 计算 |
|---|---|---|---|
| A 无等待 | 47.70 次/秒(用实测值) | 约 11 个 VU | 500 ÷ 47.70 ≈ 10.5 |
| B 带 1 秒思考 | 0.98 次/秒 | 约 511 个 VU | 500 ÷ 0.98 ≈ 510.4 |
同一个业务目标「5000 人在线」,两种脚本节奏下要拧的旋钮差了约 46 倍。所以VU 数是工具参数,不是业务结论;报告里写「用了 511 个 VU」必须同时写清「每轮思考 1 秒」。
顺带看一眼后果:如果直接把 5000 在线当成 5000 线程去配,按 A 节奏相当于想造出 5000 × 47.70 ≈ 23.8 万次/秒,是真实目标 500 次/秒的 477 倍——压测机和被测系统都会先崩,而且崩得毫无业务意义。
第 4 步:那「并发用户数」到底是多少
用 1 线程 RT = 0.02071 秒估一下:每秒来 500 个请求,每个请求在系统里停留约 0.02071 秒:
在途请求数 ≈ 500 × 0.02071 ≈ 10.4 个
也就是说,5000 人在线、每秒 500 次请求,服务端同一瞬间真正「在路上」的请求只有约 10 个。三个数摆在一起看:
| 量 | 数值 | 说明 |
|---|---|---|
| 在线用户数 | 5000 人 | 业务存量 |
| 目标到达率 | 500 次/秒 | 真实压力量纲 |
| 在途请求数(并发表 服务端口径) | ≈ 10.4 个 | 由 RT 决定,RT 越慢这个数越大 |
| 需要的 VU 数 | 11 个(A)/ 511 个(B) | 由脚本节奏决定,不是业务指标 |
第 5 步:结论该怎么写
✅ 正确:「按 5000 人在线、人均 10 秒一次操作折算,目标 500 次/秒;在 500 次/秒下 P95 = X ms、错误率 Y%、CPU Z%,满足 / 不满足 SLA;对应工具配置为 N 个 VU、每轮思考 1 秒。」
❌ 错误:「系统在 5000 并发下通过压测」——既没说清「5000」是在线人数还是线程数,也没给操作间隔,别人无法复现,业务方也无法据此决策。
五、逻辑链与易错点
完整因果链
在线人数 N(业务存量)
÷ 操作间隔 t(人的行为)
→ 每秒到达率(压力的真实量纲)
× 响应时间 RT(系统处理速度)
→ 在途请求数(服务端并发)
÷ 单 VU 产出 1/(RT+思考时间)(脚本节奏)
→ 需要开多少个 VU(工具按钮)
RT 在链上出现两次,这是最反直觉的地方:系统越慢,在途请求越多,同时每个 VU 发得越慢。所以「加线程」不一定能加压力,超过拐点后加线程只会让 RT 更长、总吞吐更低——第 0 步那四档数据就是证据。
三个最容易踩的坑
误区 1:在线用户数 = 并发用户数,直接拿在线人数配线程。 错在忽略了「人不是连发机器」。在线是存量、并发是瞬时在途量、线程是工具旋钮,三者量纲不同。纠正:先问操作间隔,把人换算成每秒请求数。你已有场景模板里只列了线程数、持续时间、Ramp-up,缺「在线人数 / 操作间隔 / 目标每秒请求数」三栏,等于默认从工具参数出发——这是流程缺口,填三行就能补上。
误区 2:把 VU 数当成压力,以为 100 个 VU 就该有 100 次/秒。 有真实数据反证:四档里 100 线程的每秒完成数只有 15.14,比 1 线程的 47.70 还低。原因不是「工具发不出请求」(100 线程档仍跑了 906 个样本),也不是「错误率上升」(四档错误率全是 0),而是排队让 RT 涨了 281 倍,每个线程被拖慢了。50 档 22.06 略高于 20 档 20.45 属于窗口波动,别当成规律。
误区 3:不同脚本 / 不同工具的 VU 数可以直接横向比较。 错在忽略「一轮多久」。VU 数只说明「同时有几个执行单元」,真实压力还要乘「每个单元一轮多快」;无等待脚本与带 1 秒思考时间的脚本,同样 50 VU 的压力差约 49 倍。纠正:比较时统一用「实测每秒请求数」,不要用 VU 数。