并发用户数、在线用户数与虚拟用户

7 阅读14分钟

一句话先说清:这三个词说的根本不是一回事——「在线」是餐厅里坐着的人,「并发」是同一秒真的在下单的人,「虚拟用户」是你雇来按剧本反复下单的试吃员;把它们当成一个数,压测的压力会配错好几倍,报告结论也会跟着写错。


一、是什么

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)每秒完成数错误率
1285520.712547.700%
201224946.63180420.450%
5013192120.68325622.060%
1009065836.27864215.140%

(这里的「每秒完成数」是整窗口平均值:样本数 ÷ 统计窗口时长。它和「瞬时每秒完成数」不是一回事。)

先自己算一遍倍数:

  • 线程: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 个 VU500 ÷ 47.70 ≈ 10.5
B 带 1 秒思考0.98 次/秒约 511 个 VU500 ÷ 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 数。