4. 性能指标提取:响应时间、吞吐量、错误率与百分位延迟
日志里埋了那么多数据,到底看哪些?
我刚开始做性能分析那会儿,也犯过傻——盯着平均响应时间看半天,觉得系统还行。结果线上用户骂翻了,说页面卡得要死。后来我才明白,平均值会骗人。真正能反映系统真实体验的,是那几个关键指标:RT、TPS/QPS、错误率,还有P99、P95。
这一章,咱们就把这些指标掰开揉碎,讲清楚它们怎么算、怎么看、怎么从日志里提出来。
4.1 响应时间(RT)——最直观的“慢”
响应时间,说白了就是一个请求从发出去到收到完整响应,花了多久。单位通常是毫秒。
在日志里,我们一般会在入口处打两个时间戳:
request_start:请求到达时间request_end:响应写完时间
RT = request_end - request_start。
关键点:RT 要区分“服务端处理时间”和“客户端感知时间”。如果网络开销大,客户端感知的 RT 会远大于服务端日志里记录的 RT。我一般建议两端都埋点,对比着看。
举个例子,一条典型的访问日志:
2025-03-21 10:00:01.234 | GET /api/order/list | user_id=1024 | rt=342ms
这里的 rt=342ms 就是服务端视角的响应时间。如果这个接口的 RT 持续超过 500ms,就该查查了。
4.2 吞吐量(TPS/QPS)——系统到底能扛多少
吞吐量衡量的是系统在单位时间内能处理多少请求。
- QPS(Queries Per Second):每秒查询数,常用于读接口
- TPS(Transactions Per Second):每秒事务数,常用于写操作或完整业务链路
其实很多场景下大家混着用,不用太纠结。关键是统计口径要一致。
怎么从日志里算?很简单:
QPS = 总请求数 / 时间窗口(秒)
比如,1分钟内日志里出现了 6000 条请求记录,那 QPS 就是 100。
我的习惯:统计吞吐量时,时间窗口别用太短(比如1秒),容易抖动。我一般取 1分钟 或 5分钟 的窗口,算出来的值更稳。如果要做毛刺分析,再切到 10秒 窗口。
吞吐量和响应时间之间有个经典关系——当系统接近瓶颈时,RT 会急剧上升。你想想看,如果 QPS 从 100 涨到 200,RT 从 50ms 涨到 80ms,还算正常。但如果 QPS 从 200 涨到 210,RT 直接飙到 500ms,那说明系统已经快扛不住了。
4.3 错误率——别让“成功”蒙住眼
错误率 = 错误请求数 / 总请求数 × 100%。
日志里常见的错误状态码:
- 4xx:客户端问题(如 404、429)
- 5xx:服务端问题(如 500、502、503)
- 超时:请求没在规定时间内返回
我一般会分开统计:
- 业务错误率:比如下单失败、支付超时,这些需要业务日志配合
- 系统错误率:5xx 和超时,直接反映系统健康度
我曾经踩过的坑:有一次线上告警说错误率 0.5%,看着不高。结果一查,是某个核心接口的 5xx 全被重试机制吞掉了,日志里只记了最后一次成功。所以,错误率统计一定要在“原始请求”层面做,别被重试逻辑污染了。
4.4 P99/P95 延迟——平均值的“照妖镜”
平均 RT 是 200ms,但 P99 是 2s。这说明什么?99% 的请求都在 2s 以内,但平均被那 1% 的慢请求拉高了?不对,恰恰相反——是大部分请求很快,但有一小撮请求慢得离谱。
百分位延迟的计算逻辑:
- 把所有请求的 RT 从小到大排序
- P50(中位数):排在第 50% 位置的 RT
- P95:排在第 95% 位置的 RT
- P99:排在第 99% 位置的 RT
举个例子,假设有 100 个请求的 RT 数据:
| 指标 | 值 | 含义 |
|---|---|---|
| 平均 RT | 150ms | 看着还行 |
| P50 | 80ms | 一半请求在 80ms 以内 |
| P95 | 300ms | 95% 的请求在 300ms 以内 |
| P99 | 1200ms | 1% 的请求超过 1.2s |
看到没?平均 RT 150ms 掩盖了 P99 1.2s 的问题。那 1% 的用户体验极差,可能就是你的核心客户。
我的建议:做性能优化时,优先盯着 P99 打。把 P99 降下来,平均 RT 自然就跟着降了。别反过来——优化平均值,可能只是把快请求弄得更快,慢请求纹丝不动。
4.5 从日志里提取这些指标
实际操作中,我一般用两种方式:
4.5.1 实时计算(流式处理)
适合用 Flink、Spark Streaming 或 ClickHouse 的物化视图。日志进来后,直接按时间窗口聚合:
-- 伪SQL:计算每分钟的P99
SELECT
toStartOfMinute(timestamp) AS minute,
quantile(0.99)(rt) AS p99_rt,
count() AS total_requests,
sumIf(1, status >= 500) / count() AS error_rate
FROM access_log
GROUP BY minute
4.5.2 离线计算(批处理)
适合用 Hive、Spark SQL 做小时级或天级分析:
-- 计算每小时的吞吐量和P95
SELECT
date_format(timestamp, 'yyyy-MM-dd HH:00') AS hour,
count() / 3600 AS avg_qps,
percentile_approx(rt, 0.95) AS p95_rt,
sum(CASE WHEN status >= 500 THEN 1 ELSE 0 END) / count() AS error_rate
FROM access_log
GROUP BY date_format(timestamp, 'yyyy-MM-dd HH:00')
注意:百分位计算在数据量大时很吃资源。我习惯先对 RT 做分桶(比如 0-10ms、10-50ms、50-100ms……),用近似算法算 P99,精度损失不大,性能提升明显。
4.6 核心知识体系
下面这张图,把本章的核心逻辑串起来了:
4.7 避坑指南
- 别只看平均:平均值会掩盖长尾问题。P99 才是用户体验的真相。
- 时间窗口要对齐:算 QPS 时用 1 分钟窗口,算 P99 时也用 1 分钟窗口,否则指标之间没法对比。
- 错误率要分层:4xx 和 5xx 分开看。4xx 可能是客户端重试导致,5xx 才是真问题。
- 百分位计算有成本:数据量大时,用近似算法(如 TDigest)代替精确排序。
我曾经犯过的错:有一次线上 P99 从 200ms 涨到 800ms,我盯着数据库慢查询查了半天,结果发现是某个上游服务超时重试导致线程池被打满。所以,P99 异常时,先看调用链,别一头扎进单点排查。