2. 性能指标与度量:响应时间、吞吐量、资源利用率、性能基准测试

聊性能优化,第一步不是动手改代码,而是先搞清楚——你到底要优化什么?

我见过太多团队,上来就怼着某个指标猛调,结果系统反而更慢了。为什么?因为指标之间是相互影响的。你压低了响应时间,可能吞吐量就上不去了;你提升了资源利用率,响应时间可能就炸了。

所以,咱们得先建立一套统一的度量语言。今天我就把这四个最核心的指标掰开揉碎了讲清楚。

2.1 响应时间:用户感知的“快”与“慢”

响应时间,说白了就是从用户发出请求到收到完整响应所花的时间。这是最直观的指标,用户说“卡”,指的就是这个。

我个人习惯把响应时间拆成三部分来看:

  • 网络传输时间:数据在客户端和服务器之间跑的时间。这个受带宽、距离影响大。
  • 排队等待时间:请求到了服务器,但线程池满了,得等着。这是很多性能瓶颈的根源。
  • 服务处理时间:CPU真正干活的时间,比如查数据库、算逻辑。

核心观点: 优化响应时间,优先砍“排队等待时间”,因为它往往占比最大,而且最容易通过扩容或调优线程池解决。

我在项目中遇到过一个问题:一个接口平均响应时间只有50ms,但P99(99%的请求)却高达2秒。排查后发现,是某个慢查询在高峰期把数据库连接池占满了,后面的请求全在排队。你看,平均值会骗人,百分位数才是真相。

我的建议: 监控响应时间时,至少关注三个值:平均值、P95、P99。平均值代表整体体验,P95和P99代表极端情况下的体验。

2.2 吞吐量:系统能扛多少活

吞吐量,就是单位时间内系统能处理的请求数量。常见单位有QPS(每秒查询数)、TPS(每秒事务数)。

你想想看,响应时间再低,如果一次只能服务一个用户,那也没用。吞吐量决定了系统的容量。

这里有个经典公式:

吞吐量 = 并发数 / 平均响应时间

嗯,要注意,这个公式是理想情况。实际中,随着并发数增加,响应时间会上升,吞吐量会先升后降,形成一个倒U型曲线。这就是著名的“吞吐量拐点”。

我曾经踩过的坑: 有一次做压测,发现QPS到了1000就上不去了,但CPU才用了40%。我以为是代码有锁竞争,查了半天,结果是数据库连接池配小了。线程都在等连接,CPU自然闲着了。所以,吞吐量瓶颈不一定在CPU上,IO、连接池、内存都可能成为短板。

2.3 资源利用率:钱花得值不值

资源利用率,就是CPU、内存、磁盘、网络等资源被使用了多少。这个指标直接关系到你的成本。

我一般把资源利用率分成两类:

  • 计算资源:CPU、GPU。利用率高说明在干活,但长期100%说明过载了。
  • 存储与IO资源:内存、磁盘、网络。这些资源往往有“水位线”,比如内存超过80%就可能触发GC或OOM。

这里有个常见的误区:很多人觉得CPU利用率越低越好。其实不是。如果CPU利用率长期低于20%,说明你的机器买贵了,资源在浪费。我个人习惯把CPU利用率控制在60%-80%之间,既留有余量应对突发流量,又不至于浪费。

避坑指南: 我曾经遇到一个系统,CPU利用率只有10%,但响应时间却很高。排查后发现,是磁盘IO在排队。所以,不要只看CPU,要关注“短板资源”。哪个资源利用率接近100%,哪个就是瓶颈。

2.4 性能基准测试:给系统“称体重”

基准测试,就是在可控环境下,用标准化的方法测量系统的性能指标。没有基准,你所有的优化都是“盲人摸象”。

做基准测试,我建议遵循以下步骤:

  1. 确定测试目标:是测响应时间,还是测吞吐量?目标不同,测试脚本不同。
  2. 搭建隔离环境:别在生产环境测,也别跟其他服务混在一起。干扰因素越少越好。
  3. 设计测试场景:模拟真实用户行为。比如,80%的读请求,20%的写请求。
  4. 逐步加压:从低并发开始,慢慢往上加,直到系统扛不住。记录每个并发下的响应时间和吞吐量。
  5. 分析结果:找到拐点,定位瓶颈。

下面是一个简单的压测脚本示例(用Python写的,实际工具可以用wrk、JMeter等):

# 模拟100个并发用户,持续30秒
import requests
import threading
import time

def send_request():
    start = time.time()
    resp = requests.get("http://my-api.com/endpoint")
    elapsed = time.time() - start
    print(f"响应时间: {elapsed:.3f}s, 状态码: {resp.status_code}")

threads = []
for i in range(100):
    t = threading.Thread(target=send_request)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

我的经验: 基准测试至少要跑3次,取中位数。因为第一次跑可能有缓存预热,结果会偏快。另外,一定要记录测试环境信息(CPU型号、内存大小、JDK版本等),否则结果没法复现。

2.5 四个指标的关系:一张图看懂

这四个指标不是孤立的。我画了一张图,帮你理清它们之间的关系:

响应时间 吞吐量 资源利用率 性能基准测试 反比关系 正比关系 基准测试是“秤”,用来衡量其他三个指标的变化

从图中你能看到:

  • 响应时间与吞吐量:通常是反比关系。响应时间越低,单位时间内能处理的请求就越多。
  • 资源利用率与吞吐量:正比关系。利用率越高,吞吐量越大,但超过拐点后,利用率再高也没用。
  • 基准测试:是“秤”,用来衡量其他三个指标的变化。没有基准,你没法判断优化是变好了还是变差了。

总结一下: 响应时间看用户体感,吞吐量看系统容量,资源利用率看成本,基准测试看变化。四个指标一起看,才能全面评估系统性能。

好了,这一章的内容就这些。记住,没有度量,就没有优化。下次动手改代码之前,先问问自己:我要优化哪个指标?怎么度量它?

可可精品资料库