2. 性能度量与基准测试:关键性能指标与实战方法论

性能优化这件事,说白了就是「先测量,再优化」。没有度量,就没有改进。我见过太多团队一上来就闷头改代码,改完发现性能反而更差了——为什么?因为连瓶颈在哪都没搞清楚。

这一章,我们来聊聊性能度量那些事。我会结合自己踩过的坑,把延迟、吞吐量、QPS、TP99这些指标讲透,再聊聊基准测试到底该怎么做。

2.1 关键性能指标:你到底该关注什么?

性能指标有很多,但真正关键的其实就几个。我个人习惯把指标分成两类:速度类和容量类。

2.1.1 延迟(Latency)

延迟,就是一次请求从发出到收到响应所花的时间。单位通常是毫秒(ms)或微秒(μs)。

你想想看,用户点一个按钮,等多久会不耐烦?我做过一个电商项目,首页加载从 2 秒优化到 0.8 秒,转化率直接提升了 12%。这就是延迟的威力。

关键点:延迟不是平均值说了算,尾延迟才是真正的杀手。

2.1.2 吞吐量(Throughput)

吞吐量是系统在单位时间内能处理的请求数量。常见单位有:

  • RPS(Requests Per Second):每秒请求数
  • TPS(Transactions Per Second):每秒事务数
  • QPS(Queries Per Second):每秒查询数

这三个概念经常混用,但严格来说,TPS 包含一个完整的事务(可能涉及多次查询),而 QPS 只算单次查询。我在项目中一般用 QPS 来衡量 API 网关,用 TPS 来衡量数据库写入。

2.1.3 QPS 与 TP99

QPS 很好理解,就是每秒能扛多少请求。但 TP99 是什么?

TP99(Top Percentile 99)意思是:99% 的请求延迟都在这个值以下。举个例子,TP99 = 200ms,说明只有 1% 的请求会超过 200ms。

为什么要关注 TP99?因为平均值会骗人。我曾经遇到一个系统,平均延迟只有 50ms,但 TP99 高达 2 秒。这意味着每 100 个用户里就有 1 个在「吃屎」——用户体验极差。

指标 含义 典型值 关注点
平均延迟 所有请求延迟的算术平均 50ms 容易被极端值拉偏
TP50 50% 请求的延迟上限 30ms 反映大多数用户感受
TP99 99% 请求的延迟上限 200ms 反映长尾问题
TP999 99.9% 请求的延迟上限 1s 极端情况下的体验
我的习惯:线上监控至少看 TP99,核心接口看 TP999。平均值?我基本不看。

2.2 基准测试方法论:别让测试结果骗了你

基准测试(Benchmark)就是给系统「称体重」。但称体重也有讲究——你站错秤、穿错鞋,结果都不准。

2.2.1 测试环境要隔离

这个坑我踩过。有一次我在开发环境做压测,结果发现 QPS 死活上不去。折腾了半天,才发现隔壁团队正在跑一个大数据任务,把 CPU 吃满了。

所以,基准测试一定要在隔离环境中进行:

  • 专用机器,不混部
  • 关闭不必要的后台进程
  • 网络带宽要充足
  • 监控系统本身的开销要扣除

2.2.2 预热(Warm-up)必不可少

JVM 有 JIT 编译,数据库有缓存,连接池有初始化。如果不预热,你测出来的其实是「冷启动性能」,不是真实性能。

我一般会先跑 1-2 分钟的预热请求,等指标稳定了再开始记录数据。

2.2.3 测试时长要足够

跑 10 秒就下结论?太草率了。我建议:

  • 最少跑 3 分钟
  • 观察是否有 GC 抖动
  • 观察是否有连接泄漏
  • 多次测试取中位数

2.2.4 控制变量法

一次只改一个参数。改完代码、改配置、改硬件——三个一起改,出了问题你根本不知道是谁的锅。

警告:千万不要在生产环境做基准测试!我曾经见过有人直接在线上压测,结果把数据库打挂了,整个业务停了 20 分钟。

2.3 常用基准测试工具

工具不在多,在于会用。我常用的就这几个:

2.3.1 Apache Bench (ab)

最简单的 HTTP 压测工具,适合快速验证。

# 并发 100,总共 10000 个请求
ab -n 10000 -c 100 http://localhost:8080/api/test

优点:轻量、自带。缺点:只能测 HTTP,不支持复杂场景。

2.3.2 wrk

比 ab 更现代,支持 Lua 脚本,可以模拟复杂请求。

# 4 个线程,100 个连接,持续 30 秒
wrk -t4 -c100 -d30s http://localhost:8080/api/test

我个人很喜欢 wrk,因为它能输出延迟分布,直接看到 TP99。

2.3.3 JMeter

功能最全,但配置也最复杂。适合做全链路压测。

  • 支持多种协议(HTTP、JDBC、MQ)
  • 有图形界面
  • 可以分布式压测

嗯,这里要注意:JMeter 本身也是 Java 写的,压测机本身的内存和 GC 也会影响结果。我一般会用多台机器做分布式压测。

2.3.4 工具对比

工具 适用场景 学习成本 性能
ab 快速验证 低 一般
wrk 接口压测 中 高
JMeter 全链路压测 高 中
Locust Python 项目 中 中

2.4 基准测试流程:一个实战案例

光说不练假把式。我拿一个真实案例来演示:

场景:优化一个用户查询接口,目标是 QPS 从 500 提升到 2000。

步骤:

  1. 搭建隔离环境:一台 4C8G 的云服务器,关闭所有非必要服务。
  2. 预热:先跑 2 分钟请求,让 JIT 编译完成。
  3. 基线测试:用 wrk 测出当前性能。
  4. 优化:加缓存、改 SQL、调连接池。
  5. 对比测试:同样条件再测一次。
  6. 分析结果:看 QPS 和 TP99 的变化。
# 基线测试结果
wrk -t4 -c100 -d30s http://localhost:8080/user/query
Requests/sec:    512
Latency Distribution:
  50%   45ms
  99%  210ms

# 优化后测试结果
wrk -t4 -c100 -d30s http://localhost:8080/user/query
Requests/sec:   2150
Latency Distribution:
  50%   12ms
  99%   45ms

你看,QPS 从 512 涨到了 2150,TP99 从 210ms 降到了 45ms。这就是基准测试的价值——用数据说话。

避坑指南:我曾经在优化后只测了一次就宣布成功,结果第二天发现 TP99 又涨回去了。后来才意识到是 GC 参数没调好,跑久了就出问题。所以,长时间压测才能暴露真实问题。

2.5 小结

这一章我们聊了:

  • 延迟、吞吐量、QPS、TP99 这些核心指标
  • 基准测试的方法论——环境隔离、预热、控制变量
  • 常用工具的选择和使用
  • 一个完整的实战案例

记住一句话:没有度量,就没有优化。下一章,我们会深入代码层面,看看如何用 Profiling 工具找到真正的瓶颈。