「平均等 3 分钟」几乎没有信息
排队时间不是围着平均值上下抖动的。它是一堆零,加上一条又长又薄的尾巴。你的用户体验、你的超时设置、你的告警阈值,全都住在那条尾巴上——而平均值恰好是唯一一个看不见尾巴的统计量。
一条队,利用率 80%,到达和服务都是随机的。平均等待是 4 个服务时长(第 1 章那个数)。
现在问:有多大比例的人,等待时间低于这个平均值?
顺手再估第二个:这条队的 p99 等待是平均值的几倍?
平均值是怎么骗人的
「平均等待 4 个服务时长」这句话在你脑子里长什么样?
大概是这样:大部分人等 3 到 5,少数人等 2 或 6,极少数运气特别差的等到 8。一个钟形的东西,围着 4。
真实形状完全不是这样。
ρ=0.8 时的真实分布是这样的:
- 20.0% 的人等待恰好是零——他们来的时候机器正好空着,直接开始。这不是「等得很少」,是「一点没等」。
- 中位数是 2.350,只有平均值的一半多一点。
- p90 是 10.397。
- p99 是 21.910——平均值的 5.48 倍。
- p99.9 是 33.423。
- 而在三十万次模拟里,最惨的那一位等了 49.7。
把这些数字放在一起看:「平均 4」这个数,同时代表着「五分之一的人是 0」和「百分之一的人是 22」。它落在两群人中间的空地上。
而且有 63.2% 的人低于平均值。这个比例本身就说明了形状——在一个对称分布里它应该是 50%。
M/M/1 队列的等待时间有一个非常干净的闭式解,它把上面所有数字都解释清楚了:
P(等待 = 0) = 1 − ρ ← 一大块概率质量堆在零上 P(等待 > t) = ρ × e^(−(μ−λ)t) ← 剩下的是一条指数尾
这是一个混合分布:一部分是「点质量」(一大堆人的等待精确为零),一部分是「连续的指数尾」。它长得一点都不像钟形,也没有任何理由长得像钟形。
而指数尾的意思是:它衰减得很慢,而且没有硬上限。永远存在一个更倒霉的人。
为什么会有一条指数尾
这不是数学巧合,背后有一个很物理的机制,叫忙期(busy period)。
队列的一生是这样的:空 → 来了一个人 → 在他被服务的时候又来了人 → 队排起来 → 一直排到某个时刻,队终于空了 → 空 → 又来一个人……
中间那段「一直不空」的时间叫一个忙期。关键在于:忙期的长度是自我放大的。队越长,清空它需要的时间越久;而清空它的时间越久,期间来的人就越多,队就更长。
# 一个忙期的样子(ρ = 0.8)
空 ▏◉ ◉◉ ◉◉◉ ◉◉◉◉ ◉◉◉ ◉◉ ◉ ▏空 ▏◉ ▏空空空 ▏◉◉◉◉◉◉◉◉◉◉◉◉ …
└──── 一个普通忙期 ────┘ └短┘ └──── 一个倒霉的长忙期 ──…
# 落在长忙期里的人,等待时间是长忙期里所有人一起决定的
# 而长忙期出现的概率虽然低,却不会低到可以忽略 —— 那就是那条尾巴
ρ 越接近 1,忙期越难结束,尾巴就越长。这也解释了 p99 的来源:p99 不是「运气差」,它是「掉进长忙期」的概率。而长忙期出现的概率完全由 ρ 和变异性决定——它可以算,也可以设计。
M/M/1 的等待时间分位数有闭式解,你可以拿计算器直接用:
# 想知道 p 分位的等待时间(p 要大于 1−ρ,否则答案就是 0) Wq(p) = ln( ρ ÷ (1−p) ) ÷ (μ − λ) # 举例:ρ=0.8,一次服务 1 个时间单位(μ=1,λ=0.8) Wq(0.50) = ln(0.8 ÷ 0.50) ÷ 0.2 = 2.350 Wq(0.99) = ln(0.8 ÷ 0.01) ÷ 0.2 = 21.910 Wq(0.999)= ln(0.8 ÷ 0.001) ÷ 0.2 = 33.423 # 注意 ln 里面:分位数越靠尾,只是对数式地变大。 # 这是好消息也是坏消息 —— # 好消息:从 p99 到 p99.9 只多了 11.5,不是十倍。 # 坏消息:那个 (μ − λ) 在分母上。它就是余量。余量减半,整条尾巴翻倍。
最后那句话值得记住:所有分位数都被同一个分母 μ − λ 缩放。这个分母就是「每单位时间能多消化多少活」,也就是余量。你不需要分别优化 p50、p95、p99——它们是同一根杠杆上的刻度。
该看哪个数
那么该报什么?这里有一个实践上的排序,从最没用到最有用:
- 平均值——几乎唯一的用途是喂进 Little 定律。不要用它做 SLA,不要用它做告警。
- 中位数(p50)——回答「典型的一次体验是什么样」,比平均值诚实得多。
- p99 / p99.9——回答「最糟的那部分体验是什么样」。这是用户投诉的来源,也是超时该设在哪的依据。
- 整条直方图——唯一能让你看出「是一条尾巴,还是两个峰」的东西。两个峰意味着你的流量里混了两类完全不同的请求,那是另一个故事(第 18 章、第 19 章)。
另外还有一个特别值得报的数,几乎没人报:「不用等的人占多少」。它精确地等于 1 − ρ,也就是余量。这个数是「有多满」和「用户体验」之间最直白的一座桥:余量 20%,就有 20% 的人享受到零等待。
「长尾」这个词在产品和商业语境里被用成了褒义(长尾市场、长尾需求)。在这里它是纯粹的成本。
而且要区分两种不同程度的尾:
- 指数尾(本章这条):衰减得慢,但方差是有限的,公式算得出来。
- 重尾 / 幂律尾(第 5 章会见到):衰减得更慢,方差可以是无穷的。此时「平均值」不但没用,连「方差」这个概念本身都失效了,本书大部分公式在那里直接不成立。
真实的软件系统里,请求耗时经常是重尾的(想想一个查询扫了全表)。所以这本书的公式在实战中往往是乐观的下界。
为什么你的超时总是设错。常见做法是「按平均延迟的 3 倍设超时」。在 ρ=0.8 时,平均值的 3 倍大约落在 p95 附近——意味着你主动放弃了 5% 的请求,而且是在系统完全健康的时候。更糟的是,超时会触发重试,重试增加负载,负载推高 ρ,ρ 推长尾巴,更多请求超时。这个正反馈叫重试风暴,第 21 章会算它的账。
为什么「99% 的用户没受影响」是错的说法。一次用户操作往往触发几十个后端请求。每个请求 p99,几十个一起,用户碰到至少一个慢请求的概率高得多——第 16 章会算出那个数字,它比大多数人猜的大得多。
地铁换乘。「平均换乘等待 3 分钟」对通勤族毫无意义。他们规划时间用的是「最坏情况」,因为迟到的成本是不对称的。凡是成本不对称的场景,平均值天然就是错的统计量。
μ − λ 缩放。你没法单独优化平均值而不动 p99,也没法只治 p99 而不动余量。
更重要的是:p99 才是那个决定用户是否留下的数。一个每天用 100 次的功能,p99 = 1% 意味着用户平均每天碰到一次糟糕体验。他不会记得那 99 次顺畅,他会记得那一次。
还有一个陷阱:很多人以为「优化尾部」需要专门的技术手段。其实第 10 章会证明,在高利用率区间,只要加一点点产能,整条分布(包括尾巴)会一起往下掉——而且加的量少得让人吃惊。
B大约 63%。模拟三十万个请求量出来是 63.2%。而第二问的答案是 5.48 倍:p99 是 21.910,平均值是 4.000。
63% 这个数不是巧合。指数分布有一个著名的性质:低于均值的概率恰好是 1 − 1/e ≈ 63.2%。等待时间的尾巴是指数的,所以这个数字在这里冒了出来。
1 − ρ = 20% 的,是「等待恰好为零的人的比例」。
D 「大约 95%」——高估了偏斜程度。指数尾虽然长,但它比幂律尾温和得多;要到 95% 这个程度,得是第 5 章那种重尾分布。
这一章的一句话
排队时间是「一大堆零 + 一条指数尾」,平均值恰好落在没人住的地方;而所有分位数共用一个分母,那个分母就是余量。
卷 I 到此结束。你已经知道了排队有两个成因,也知道了它的形状。下一卷专攻第二个成因:不齐。第 5 章先把它变成一个可以算、可以量、可以挂在监控上的数——你会看到一个 c² 等于 7.424 的分布(90% 快 + 10% 慢一百倍),以及一个 c² 等于无穷的分布,而后者在真实系统里一点都不罕见。