16、性能基准测试:延迟百分位(P50/P99/P999),吞吐量测试,压力测试场景设计
做交易系统,最怕什么?
怕慢。怕关键时刻掉链子。
我见过太多团队,代码写得花里胡哨,一上生产环境就崩。为什么?因为没有做性能基准测试。说白了,你连自己的系统能扛多少压力都不知道,就敢拿真金白银去跑?
这一章,我们就来聊聊怎么做性能基准测试。我会把我在项目中踩过的坑、总结的经验,都掏出来给你看。
16.1 为什么性能测试对做市商系统如此重要?
做市商系统,本质上是在跟时间赛跑。
你的报价比别人慢1毫秒,可能就成交不了。你的系统在高并发下延迟飙升,可能就会产生大量滑点。我有个朋友,他们的系统在正常行情下P99延迟只有2毫秒,结果某天突发行情,P99直接飙到200毫秒,当天亏了上百万。
所以,性能测试不是可选项,是必选项。
核心指标:
- 延迟百分位:P50(中位数)、P99(99%请求)、P999(99.9%请求)
- 吞吐量:每秒能处理多少订单(TPS/QPS)
- 压力场景:系统在极限负载下的表现
16.2 延迟百分位:别被平均值骗了
很多人喜欢看平均延迟。我告诉你,平均延迟是最大的谎言。
举个例子:系统处理1000个请求,999个都是1毫秒,但有1个是1000毫秒。平均延迟是多少?约2毫秒。看起来不错对吧?但那个1000毫秒的请求,可能就是你丢单的原因。
所以,我们要看百分位。
16.2.1 P50、P99、P999 到底代表什么?
| 指标 | 含义 | 做市商场景下的意义 |
|---|---|---|
| P50 | 50%的请求在这个延迟内完成 | 代表系统日常表现,一般要求 < 1ms |
| P99 | 99%的请求在这个延迟内完成 | 代表系统在压力下的表现,要求 < 5ms |
| P999 | 99.9%的请求在这个延迟内完成 | 代表极端情况,要求 < 20ms,否则可能丢单 |
我的经验: 我曾经遇到过一个系统,P50只有0.5ms,看起来完美。但P999高达500ms。查了半天,发现是垃圾回收(GC)导致的。后来我们换用了低延迟的内存分配器,才把P999压到10ms以内。
16.2.2 如何测量延迟百分位?
测量延迟,不能只测一次。你需要一个高精度的计时器,比如C++里的 std::chrono::high_resolution_clock,或者Python里的 time.perf_counter_ns()。
下面是一个简单的C++延迟测量代码示例:
#include <chrono>
#include <vector>
#include <algorithm>
// 记录延迟
std::vector<long long> latencies;
auto start = std::chrono::high_resolution_clock::now();
// 执行你的交易逻辑
process_order();
auto end = std::chrono::high_resolution_clock::now();
auto latency = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
latencies.push_back(latency);
// 计算百分位
std::sort(latencies.begin(), latencies.end());
size_t n = latencies.size();
long long p50 = latencies[n * 50 / 100];
long long p99 = latencies[n * 99 / 100];
long long p999 = latencies[n * 999 / 1000];
注意: 测量本身也会引入开销。我建议使用无锁的环形缓冲区来记录延迟,避免锁竞争影响测量结果。
16.3 吞吐量测试:你的系统能扛多少?
延迟是看单个请求快不快,吞吐量是看系统整体能处理多少请求。
做市商系统,吞吐量通常用TPS(每秒交易笔数)来衡量。一个中等规模的做市商,可能每秒要处理几千笔订单。如果是高频做市,可能要到几万甚至几十万。
16.3.1 吞吐量测试的设计思路
吞吐量测试,说白了就是压测。你需要模拟大量客户端,同时向系统发送订单。
我个人习惯用以下步骤:
- 确定目标:比如目标TPS是10000
- 逐步加压:从1000 TPS开始,每次增加1000,观察系统表现
- 记录拐点:当延迟开始急剧上升时,就是系统的极限吞吐量
下面是一个Python的简单压测脚本框架:
import asyncio
import time
async def send_order(client, order):
start = time.perf_counter_ns()
await client.send(order)
end = time.perf_counter_ns()
return end - start
async def benchmark(target_tps, duration=10):
tasks = []
latencies = []
start_time = time.time()
while time.time() - start_time < duration:
# 控制发送速率
batch_start = time.perf_counter_ns()
for _ in range(target_tps // 10): # 每100ms发一批
task = asyncio.create_task(send_order(client, order))
tasks.append(task)
# 等待这批任务完成
results = await asyncio.gather(*tasks)
latencies.extend(results)
# 控制节奏
elapsed = (time.perf_counter_ns() - batch_start) / 1e9
await asyncio.sleep(max(0, 0.1 - elapsed))
return latencies
避坑指南: 我曾经犯过一个错误——在压测时,客户端和服务器跑在同一台机器上。结果测出来的数据完全不准,因为客户端占用了大量CPU资源。记住,压测客户端一定要用独立的机器。
16.4 压力测试场景设计:模拟真实战场
压力测试不是随便发几个请求就完事了。你需要设计真实的场景。
16.4.1 常见的压力场景
| 场景 | 描述 | 预期结果 |
|---|---|---|
| 正常行情 | 模拟日常交易量,比如每秒5000笔 | P99 < 5ms,无丢单 |
| 突发行情 | 模拟新闻发布时的瞬间流量,比如每秒50000笔 | 系统不崩溃,P999 < 50ms |
| 持续高压 | 长时间(比如1小时)维持高负载 | 无内存泄漏,延迟不持续上升 |
| 网络抖动 | 模拟网络延迟增加、丢包 | 系统能自动重连,不丢单 |
16.4.2 如何设计压力测试脚本?
我建议用以下框架:
# 压力测试场景设计示例
class StressTestScenario:
def __init__(self, name, tps_profile, duration):
self.name = name
self.tps_profile = tps_profile # 一个函数,返回当前时间点的目标TPS
self.duration = duration
def run(self):
start_time = time.time()
while time.time() - start_time < self.duration:
current_tps = self.tps_profile(time.time() - start_time)
# 按current_tps发送订单
self.send_batch(current_tps)
time.sleep(0.1) # 每100ms调整一次
# 突发行情场景
def flash_crash_profile(t):
if t < 5:
return 5000 # 前5秒正常
elif t < 10:
return 50000 # 5-10秒突发
else:
return 5000 # 之后恢复正常
scenario = StressTestScenario("flash_crash", flash_crash_profile, 30)
scenario.run()
重要: 压力测试一定要在隔离环境进行。我见过有人直接在生产环境做压测,结果把整个交易系统搞挂了。记住,测试环境要和生产环境完全隔离,但硬件配置要尽可能一致。
16.5 性能基准测试的整体流程
说了这么多,我们来总结一下完整的测试流程。我画了一张图,方便你理解:
这张图展示了完整的测试流程。注意看,第7步优化后,要回到第2步重新设计场景。这是一个循环迭代的过程,不是一次性的。
16.6 常见问题与避坑指南
最后,分享几个我踩过的坑:
- 不要用平均值:我见过太多人只看平均延迟,结果系统在极端情况下崩了。记住,P99和P999才是关键。
- 预热很重要:系统刚启动时,JIT编译、缓存填充都需要时间。我习惯先跑5分钟预热,再开始记录数据。
- 监控系统本身的开销:如果你用复杂的监控框架,它本身就会拖慢系统。我建议用轻量级的日志方式,比如用mmap写文件,避免锁竞争。
- 网络延迟不可忽视:有时候不是你的系统慢,而是网络慢。记得在测试中模拟不同的网络条件。
总结一下:
性能基准测试,说白了就是给你的系统做体检。延迟百分位告诉你系统快不快,吞吐量告诉你系统能扛多少,压力测试告诉你系统会不会崩。这三者缺一不可。
我建议你从最简单的场景开始,逐步增加复杂度。不要一上来就想测几十万TPS,先把基础打牢。
无相订单流研究社 微信Lucian808555