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 吞吐量测试的设计思路

吞吐量测试,说白了就是压测。你需要模拟大量客户端,同时向系统发送订单。

我个人习惯用以下步骤:

  1. 确定目标:比如目标TPS是10000
  2. 逐步加压:从1000 TPS开始,每次增加1000,观察系统表现
  3. 记录拐点:当延迟开始急剧上升时,就是系统的极限吞吐量

下面是一个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 性能基准测试的整体流程

说了这么多,我们来总结一下完整的测试流程。我画了一张图,方便你理解:

性能基准测试流程 1. 确定测试目标 2. 设计测试场景 3. 搭建测试环境 4. 执行测试 5. 收集数据 6. 分析结果 7. 优化系统 数据采集要点 • 延迟:P50/P99/P999 • 吞吐量:TPS/QPS • 资源:CPU/内存/网络

这张图展示了完整的测试流程。注意看,第7步优化后,要回到第2步重新设计场景。这是一个循环迭代的过程,不是一次性的。

16.6 常见问题与避坑指南

最后,分享几个我踩过的坑:

  • 不要用平均值:我见过太多人只看平均延迟,结果系统在极端情况下崩了。记住,P99和P999才是关键。
  • 预热很重要:系统刚启动时,JIT编译、缓存填充都需要时间。我习惯先跑5分钟预热,再开始记录数据。
  • 监控系统本身的开销:如果你用复杂的监控框架,它本身就会拖慢系统。我建议用轻量级的日志方式,比如用mmap写文件,避免锁竞争。
  • 网络延迟不可忽视:有时候不是你的系统慢,而是网络慢。记得在测试中模拟不同的网络条件。

总结一下:

性能基准测试,说白了就是给你的系统做体检。延迟百分位告诉你系统快不快,吞吐量告诉你系统能扛多少,压力测试告诉你系统会不会崩。这三者缺一不可。

我建议你从最简单的场景开始,逐步增加复杂度。不要一上来就想测几十万TPS,先把基础打牢。


无相订单流研究社 微信Lucian808555