吞吐压测基础:吞吐量、QPS/TPS与核心指标
各位同学,今天咱们聊聊吞吐压测最基础的东西。说实话,我见过不少刚入行的朋友,一上来就盯着延迟看,或者只关心并发数,结果压测报告出来,老板问「系统到底能扛多少量?」——答不上来。
嗯,这就是没搞清楚吞吐量的意义。我个人习惯,做任何压测之前,先问自己三个问题:测什么?怎么测?结果怎么看? 吞吐量就是那个「测什么」的核心。
什么是吞吐量?
吞吐量,说白了就是系统在单位时间内能处理多少请求。你想想看,一个收费站,一分钟能过多少辆车?那就是吞吐量。
在计算机世界里,常见的单位有:
- QPS(Queries Per Second):每秒查询数。常用于读多写少的场景,比如搜索引擎、缓存系统。
- TPS(Transactions Per Second):每秒事务数。一个事务可能包含多个操作,比如下单、支付、扣库存。常用于金融、电商系统。
我在项目中遇到过最典型的误解:有人把 QPS 和 TPS 混着用。其实区别很明显——QPS 侧重单次查询,TPS 侧重完整业务链路。比如你点个外卖,从浏览到下单,可能涉及 10 次 QPS,但只算 1 个 TPS。
核心公式:
吞吐量 = 并发数 × (1 / 平均响应时间)
或者更直观:吞吐量 = 并发数 / 平均响应时间
这个公式我建议你刻在脑子里。它揭示了三个变量之间的铁三角关系——你动任何一个,另外两个都会跟着变。
QPS 和 TPS 到底怎么算?
咱们直接上代码。这是我常用的一个压测脚本片段,用 Python 模拟计算 QPS:
import time
import threading
# 模拟一个简单的接口处理
def handle_request():
# 模拟处理耗时 50ms
time.sleep(0.05)
return "ok"
# 压测主逻辑
def run_load_test(concurrency, duration_seconds):
total_requests = 0
start_time = time.time()
def worker():
nonlocal total_requests
while time.time() - start_time < duration_seconds:
handle_request()
total_requests += 1
threads = []
for _ in range(concurrency):
t = threading.Thread(target=worker)
threads.append(t)
t.start()
for t in threads:
t.join()
elapsed = time.time() - start_time
qps = total_requests / elapsed
print(f"并发数: {concurrency}, 总请求: {total_requests}, QPS: {qps:.2f}")
# 测试不同并发下的 QPS
run_load_test(10, 5) # 10 并发
run_load_test(50, 5) # 50 并发
run_load_test(100, 5) # 100 并发
这段代码很简单,但能说明问题。你跑一下就会发现:并发数不是越高越好。我曾经在一个压测项目里,把并发从 100 调到 500,结果 QPS 反而下降了——因为线程切换开销把 CPU 吃满了。
压测的核心三指标
做吞吐压测,你不能只看 QPS。我个人习惯盯着三个指标:延迟、并发、成功率。少一个都不行。
| 指标 | 含义 | 常见坑点 |
|---|---|---|
| 延迟 | 单个请求从发出到收到响应的时间 | 只看平均值会掩盖长尾问题 |
| 并发 | 同一时刻系统正在处理的请求数 | 并发高不等于吞吐高 |
| 成功率 | 成功响应的请求占比 | 压测时容易忽略错误码 |
延迟:别被平均值骗了
延迟我一般看三个分位值:P50、P99、P999。P50 是大多数用户的体验,P99 是 99% 的用户体验,P999 是极限情况。
我记得有一次压测一个推荐系统,平均延迟只有 20ms,看起来很美。但一看 P99,好家伙,800ms。为什么?因为缓存命中率在高峰期下降了,导致部分请求走了全量计算。这就是典型的「平均掩盖问题」。
我的习惯:压测报告里必须包含 P99 延迟。如果 P99 超过平均值的 5 倍,说明系统存在严重的抖动问题,需要排查。
并发:不是越大越好
并发数代表系统同时承载的请求量。但你要明白,并发和吞吐不是线性关系。当并发超过某个阈值,系统资源(CPU、内存、连接池)会开始争抢,吞吐量反而下降。
我曾经优化过一个数据库连接池,默认配置是 200 个连接。压测发现 QPS 只有 3000。我把连接数降到 50,QPS 反而涨到了 4500。为什么?因为连接太多导致上下文切换频繁,数据库也在排队。你想想看,200 个人同时打电话给一个人,那人接得过来吗?
成功率:压测的底线
这个指标最容易被忽视。很多人压测只看 QPS 高不高,结果一看日志,一半请求返回了 500 错误。那 QPS 再高有什么用?
我建议压测时设置一个成功率阈值,比如 99.9%。一旦低于这个值,压测应该自动停止。我曾经在压测一个支付系统时,发现成功率从 99.99% 掉到了 99.5%,排查后发现是数据库连接池满了,新请求直接超时。如果没盯着成功率,这个 bug 可能就上线了。
避坑指南:我曾经犯过一个错——压测时只统计了 200 状态码,忽略了 302 重定向。结果 QPS 虚高,因为很多请求被重定向到了 CDN,根本没打到后端。所以,成功率一定要基于业务逻辑定义,而不是 HTTP 状态码。
知识体系总览
下面这张图是我自己画的,把吞吐压测的核心逻辑串起来了。你可以把它当作本章的思维导图:
这张图把本章的核心逻辑讲清楚了。你从中心出发,左边是延迟,中间是并发,右边是成功率。三个指标互相牵制,缺一不可。
小结
吞吐压测不是跑个脚本看数字那么简单。你得理解吞吐量、QPS/TPS 的本质,盯住延迟、并发、成功率这三个铁三角。我个人经验是:先定目标(比如目标 QPS 5000),再定约束(P99 延迟 < 200ms,成功率 > 99.9%),最后才是调参数。
嗯,这一章就到这里。记住那个公式,记住三个指标,后面咱们聊具体怎么压、怎么优化。
一句话总结:吞吐量是系统的「体力」,延迟是「反应速度」,并发是「同时能接多少活」,成功率是「活干得对不对」。四者缺一不可。