1. 压力测试基础:并发交易系统的核心概念
大家好,我是你们的讲师。今天咱们聊聊并发交易系统的压力测试。说实话,我入行那会儿,对压力测试的理解特别肤浅——以为就是多开几个线程发请求。直到有一次线上事故,让我彻底改变了看法。
那次事故是这样的:一个券商系统上线前,开发说测过了没问题。结果开盘半小时,系统直接挂了。后来复盘发现,他们只测了100个并发,但实际峰值到了2000。嗯,这就是典型的压力测试没做到位。
什么是并发交易系统?
并发交易系统,说白了就是能同时处理大量交易请求的系统。你想想看,双十一零点,几亿人同时下单;股票开盘瞬间,成千上万笔买卖单同时涌入。这些场景背后,都是并发交易系统在支撑。
我个人习惯把并发交易系统拆成三个核心部分:
- 接入层:负责接收请求,做初步校验。比如限流、鉴权这些。
- 业务层:处理核心交易逻辑。像订单匹配、资金结算。
- 数据层:保证数据一致性。数据库、缓存、消息队列都在这一层。
我在项目中遇到过最头疼的问题,就是这三层之间的协调。接入层扛住了,业务层却崩了;业务层优化好了,数据库又成了瓶颈。所以做压力测试,不能只看单点,要看整体。
核心观点:并发交易系统的本质是「在有限资源下,最大化吞吐量并保证数据一致性」。
压力测试的目标与意义
压力测试的目标,其实就三个字:找极限。找到系统在什么情况下会崩,崩之前是什么表现。
具体来说,压力测试要回答这几个问题:
- 系统能扛多少并发? 比如1000个用户同时交易,系统还能正常响应吗?
- 响应时间会变多慢? 正常情况下50ms,并发上来后会不会变成5秒?
- 错误率会飙升吗? 超时、连接失败、数据不一致,这些错误在什么阈值下出现?
- 资源消耗怎么样? CPU、内存、网络IO,哪个先撑不住?
我曾经帮一个P2P平台做压测,发现他们的数据库连接池只配了20个。并发一上来,请求全在等连接,响应时间从200ms直接飙到30秒。这就是典型的「没测不知道,一测吓一跳」。
我的建议:压力测试不是「测一次就完事」。我习惯在每个迭代都跑一遍压测,哪怕只改了业务代码。因为很多时候,小改动会引发大问题。
核心指标:TPS、响应时间、错误率
这三个指标,是压力测试的「三驾马车」。少了任何一个,你都无法全面评估系统性能。
1. TPS(每秒事务数)
TPS就是系统每秒能处理多少个完整的事务。注意是「完整事务」,不是单个请求。比如一笔交易,可能包含查询、校验、扣款、写日志等多个步骤,只有全部完成才算一个事务。
我见过很多新手把QPS和TPS搞混。QPS只管请求量,TPS管的是业务完成量。举个例子:你发了100个请求,但只有80个成功返回了完整结果,那TPS就是80,不是100。
避坑指南:我曾经遇到一个团队,压测报告上写着TPS 5000,结果上线后实际只有800。为什么?因为他们把「请求到达」算成了「事务完成」。记住:TPS只看成功返回的完整事务。
2. 响应时间
响应时间就是用户发请求到收到响应的时间差。通常关注三个值:
| 指标 | 含义 | 我的经验值 |
|---|---|---|
| 平均响应时间 | 所有请求的平均值 | 容易受极端值影响,参考价值有限 |
| P95响应时间 | 95%的请求都在这个时间内完成 | 我一般重点看这个,代表大多数用户的体验 |
| P99响应时间 | 99%的请求都在这个时间内完成 | 用来排查长尾问题,比如慢查询 |
我个人习惯把P95作为核心指标。为什么?因为平均响应时间容易被几个超长请求拉高,而P99又太苛刻。P95是个不错的平衡点。
3. 错误率
错误率就是失败请求占总请求的比例。常见的错误类型有:
- 超时错误:请求发出去,等了好久没回音
- 连接错误:根本连不上服务器
- 业务错误:请求成功了,但返回了错误码(比如余额不足)
- 数据一致性错误:扣款成功了,但订单没生成(这种最要命)
我记得有一次压测,错误率一直维持在5%左右。开发说「5%可以接受」。我追问了一句:「这5%是哪些请求?」一查发现,全是金额超过10万的大额交易。嗯,这要是上线了,5%的大额交易失败,客户不得炸锅?
重要原则:错误率不能只看整体,要按业务类型、请求来源、时间窗口分别统计。有时候1%的错误率,可能集中在最核心的业务上。
知识体系总览
下面这张图,是我自己总结的压力测试知识体系。你可以把它当成一个「地图」,后续章节都会围绕这些展开。
一个简单的压测脚本示例
光说不练假把式。我写个简单的Python脚本,演示怎么测TPS和响应时间。这个脚本虽然简单,但核心逻辑和工业级压测工具是一样的。
import time
import threading
import requests
from statistics import mean, median
# 压测配置
TARGET_URL = "http://localhost:8080/api/trade"
CONCURRENT_USERS = 50 # 模拟50个并发用户
REQUESTS_PER_USER = 20 # 每个用户发20个请求
# 结果收集
results = []
errors = 0
lock = threading.Lock()
def send_requests(user_id):
global errors
for i in range(REQUESTS_PER_USER):
start = time.time()
try:
resp = requests.post(TARGET_URL, json={
"user_id": user_id,
"order_id": f"{user_id}_{i}",
"amount": 100
}, timeout=5)
elapsed = time.time() - start
with lock:
if resp.status_code == 200:
results.append(elapsed)
else:
errors += 1
except Exception as e:
with lock:
errors += 1
# 启动压测
threads = []
start_time = time.time()
for uid in range(CONCURRENT_USERS):
t = threading.Thread(target=send_requests, args=(uid,))
threads.append(t)
t.start()
for t in threads:
t.join()
total_time = time.time() - start_time
total_requests = CONCURRENT_USERS * REQUESTS_PER_USER
tps = total_requests / total_time
# 输出结果
print(f"总耗时: {total_time:.2f}s")
print(f"总请求数: {total_requests}")
print(f"TPS: {tps:.2f}")
print(f"平均响应时间: {mean(results)*1000:.2f}ms")
print(f"P50响应时间: {median(results)*1000:.2f}ms")
print(f"错误数: {errors}")
print(f"错误率: {errors/total_requests*100:.2f}%")
小提示:这个脚本只是演示原理。实际项目中,我建议用Locust或JMeter。它们内置了更完善的统计和报告功能。但理解这个脚本的逻辑,能帮你更好地理解压测工具的工作原理。
总结一下
这一章我们聊了三个核心点:
- 并发交易系统:三层架构,每层都可能成为瓶颈
- 压力测试的目标:找极限,而不是证明系统没问题
- 三大指标:TPS看吞吐,响应时间看体验,错误率看质量
嗯,内容就到这。记住一句话:压力测试不是「测过了就行」,而是「测透了才放心」。下一章我们会深入聊压测工具的选择和实战技巧。
公众号:蓝海资料掘金营,微信deep3321