性能基准测试:如何科学测量吞吐量

做优化,第一步不是动手改代码,而是搞清楚:你现在到底跑多快?

说白了,你得先有个基准。没有基准,你所谓的“优化”就是闭着眼睛开车。我个人习惯,任何性能优化项目开始前,至少跑三轮基准测试,取稳定值。这不是走形式,是为了后面你能理直气壮地说:“改完之后,吞吐量提升了XX%”。

什么是真正的“科学测量”?

很多人拿个压测工具,往服务器上怼一下,看到每秒请求数(QPS)出来了,就觉得完事了。嗯,这里要注意,你测出来的那个数字,可能非常不靠谱。

我在项目中遇到过一件事。有次一个同事说SDK吞吐量是 5000 TPS。我看了下他的测试环境——客户端和服务端部署在同一台机器上,连网络开销都省了。这不叫测试,这叫自欺欺人。

科学的测量,必须满足三个条件:隔离性、可重复性、统计有效性。

基准测试三要素

  • 隔离性:被测SDK独占环境,不受其他进程干扰。
  • 可重复性:同一条件下,多次测试结果波动不超过5%。
  • 统计有效性:报告P50、P99、P999延迟,而不是只报平均值。

关键指标:你到底该看什么?

吞吐量这个词,其实是被用滥了的。它至少有三种不同含义:

指标 定义 适用场景 常见误区
QPS(每秒查询数) 单位时间完成的请求数 简单请求、无状态服务 忽略了请求复杂度的差异
TPS(每秒事务数) 单位时间完成的事务数 包含多个操作的事务场景 把单操作当成事务来算
RPS(每秒请求数) 客户端视角的请求吞吐量 负载均衡、网关层 不区分成功与失败请求

我个人更关注 RPS + P99 延迟的组合。 你想想看,光看 QPS 高没用,如果 P99 延迟从 20ms 飙到 500ms,用户体验直接崩了。我习惯把这两个指标画在同一个图表里,一眼就能看出系统在高压下的健康状况。

经验之谈:我见过有人把 QPS 从 1000 优化到 8000,结果 P99 延迟从 30ms 涨到了 2000ms。这叫优化吗?这叫把用户逼疯。

测试方法论:预热、稳态与斜坡

很多初级工程师直接开测,上来就上最大并发,结果数据出来一塌糊涂。为什么?因为 JIT、缓存池、连接池都还没准备好。

我得强调一下:预热是基准测试的前提条件。

我曾经在一个金融项目中犯过这个错。SDK 启动后直接压测,前 30 秒数据极差,但后面越来越好。如果我没做预热,就会误以为这个 SDK 只能扛 300 TPS。实际上,预热完成后它能扛 3000 TPS。差了一个数量级。

警告:不要在预热阶段就记录数据。预热数据必须丢弃。

判断预热完成的方式:连续 30 秒内,吞吐量波动幅度小于 3%。

再说说 斜坡测试(Ramp-up)。別一下子把并发扔上去。你可以像温水煮青蛙一样,慢慢增加压力。我一般设置 10 秒到 30 秒的斜坡时间,看吞吐量如何随并发变化。这能帮你找到系统的“拐点”——一旦超过这个点,延迟暴涨,吞吐量反而下降。

工具选型与实战配置

工欲善其事,必先利其器。我这里不吹哪个最好,只说说我怎么挑的。

工具 优势 劣势 我的推荐场景
wrk 轻量、高性能、支持 Lua 脚本 只能压 HTTP,功能单一 快速验证 SDK 的纯网络吞吐
JMeter 功能全面,支持各种协议 资源消耗大,配置繁琐 复杂业务流程压测
k6 脚本化、CI 友好、开源 学习曲线中等,插件生态一般 自动化回归基准测试

我个人习惯在本地先用 wrk 跑一组快速基准,确定大致的吞吐上限。然后再用 k6 集成到 CI 流程里,让每次提交代码后自动跑基准测试,防止性能回退。

一个典型的 wrk 命令

wrk -t12 -c400 -d30s --latency http://localhost:8080/api

参数说明:12 线程,400 连接,持续 30 秒,输出延迟分布。

数据记录与报告规范

跑完测试不是结束,是开始。你得把数据记录下来。我见过有人测完就关终端,下次想对比,什么都找不到。

我建议每个测试结果都保存为结构化的 JSON 文件,包含以下字段:

  • 测试时间戳和唯一 ID
  • SDK 版本号或 Git Commit ID
  • 环境配置(CPU、内存、JVM 参数等)
  • 所有延迟分位值(P50、P90、P99、P99.9)
  • 总请求数、失败请求数、错误率

这样,如果有人问你“为什么这次比上次慢了”,你能迅速定位出是代码改了还是环境变了。

一个完整的流程模板

下面是我在项目中总结出来的标准操作流程。你照着做,基本不会出差错:

  1. 准备环境:独享机器,关闭无关服务,固定 CPU 频率。
  2. 预热:跑 60 秒预压,丢弃结果。
  3. 斜坡加载:从 100 并发开始,每 10 秒增加 100,直到吞吐量不再增长。
  4. 稳态测试:在拐点附近,保持并发 3 分钟,记录数据。
  5. 重复验证:至少跑 3 轮,取中位数作为基准值。
  6. 保存报告:将 JSON 结果归档,标注当前分支和测试目的。

避坑指南:我曾经在 JDK 版本不同时跑出巨大差异。同样的代码,JDK 8 和 JDK 17 吞吐量差了 20%。记得在报告中写明 JDK 版本和 GC 策略。

本章知识体系

下面这张图,把上面讲的内容做了个串联。你对着它,就能理清整个基准测试的核心逻辑。

性能基准测试知识体系 科学测量吞吐量 隔离性 可重复性 统计有效性 QPS / TPS / RPS P99 延迟 预热 & 斜坡 工具选型 输出:稳定可重复的基准值 + 延迟分布报告 注意: 每次改代码都要重跑基准 不能复用旧数据