一句话定义:把一段时间内所有请求按耗时从快到慢排成一队,P99 就是排在第 99% 位置那个请求的耗时——也就是"最慢的那 1% 请求,大概要等多久"。这一小撮慢请求的耗时,就叫尾延迟(Tail Latency,也叫长尾延迟)。
一个打饭的比方
食堂窗口,100 个人排队。99 个人 1 分钟就打完了,有一个人因为饭卡消磁、阿姨找零、后厨补菜,等了 20 分钟。算平均:(99×1 + 20) ÷ 100 ≈ 1.2 分钟。看起来完美。但那个等了 20 分钟的人会发朋友圈骂食堂,另外 99 个人根本不知道发生过什么。
平均延迟就是那个 1.2 分钟,它把痛苦稀释掉了。P50(中位数)告诉你"典型用户"的体验,而 P99 告诉你"最倒霉的用户"的体验。产品口碑往往由后者决定。
长尾是怎么冒出来的
不是代码写得烂就一定慢,而是延迟天生有尾巴:
- 请求到达是随机的,处理能力是固定的。系统利用率越高,队列越容易堆积,等待时间呈非线性上涨——70% 负载时很稳,90% 负载时尾部可能突然炸开。
- 下游依赖会抖:数据库慢查询、缓存失效后回源、垃圾回收停顿、冷启动、跨机房网络重传。
- 重试会放大问题:一个请求慢了触发重试,重试又占资源,让更多请求变慢,形成雪崩。
和相邻概念的区别
| 指标 | 看的是什么 | 会漏掉什么 |
|---|---|---|
| 平均延迟 | 总体资源消耗的感觉 | 被海量快请求稀释,掩盖长尾 |
| P50 | 典型用户的体验 | 最差的那批人 |
| P95 / P99 | 尾部体验、SLO 依据 | 极端到 P99.9 的故障 |
| 吞吐量 | 每秒能处理多少请求 | 和延迟不是一回事,批量处理可以吞吐高但延迟大 |
| 错误率 / 超时率 | 已经失败的部分 | 慢但没超时的"灰色地带" |
关键点:尾延迟一旦超过超时阈值,就会转换成错误率。慢和挂,中间只隔着一条线。
为什么这事在 AI 场景里更扎心
大模型推理的延迟天然是长尾的:同一个 prompt,输出长度不同、batch 里拼进来的邻居不同、AI 词典:KV Cache">KV Cache 命中与否、排队深度不同,单个请求的耗时能差好几倍。用户在一次会话里会连续发很多条消息,只要撞上一次卡顿,整体印象就变成"这玩意儿很卡"。
这里有个容易被忽略的算术:如果全局有 1% 的请求很慢,一个用户发 100 次请求,至少撞上一次慢请求的概率约是 1 − 0.99¹⁰⁰ ≈ 63%。也就是说,全局 P99 的"小概率",在单个用户的体感里几乎必然发生。所以对交互式产品,首 token 延迟(TTFT)和流式输出,往往比总耗时更能救体验——先给反馈,再慢慢吐字。
对从业者的实际意义
1. 监控别只看平均值,至少把 P95 / P99 画出来,告警也挂在分位数上。
2. 服务目标(SLO)用分位数定义,比如"绝大多数请求要在可接受的阈值内返回",具体阈值按业务定,以官方监控口径为准。
3. 做容量规划时留出余量。把系统跑到 95% 利用率,平均延迟可能还很漂亮,尾部已经在崩。
4. 优化顺序是:先砍掉长尾的成因(慢查询、无上限重试、同步阻塞),再谈降平均。
对普通人来说,这也解释了一件事:为什么"平均网速"、"平均响应时间"这类数字看着挺好,用起来还是想砸手机。平均数安慰的是报表,分位数安慰的才是人。
