卷 III · 那面墙CH 12深度 12/24

同一份工作量,四种走法

这是这本书的招牌。五行数据,利用率、吞吐、平均服务时间三项完全相同——按任何一个常规监控指标看,它们是同一个系统。而平均等待从 0 走到 22 个服务时长。

★★ 招牌五种整齐程度0 到 22

▷ 先估一个数

五个系统,每一个都满足下面全部三条:

  • 平均每 1.25 个时间单位来一个请求(吞吐相同);
  • 平均每个请求花 1.00 个时间单位(速度相同);
  • 因此利用率都是 80%忙闲相同)。

它们唯一的区别是「整齐程度」:一个全定时,一个只有到达随机,一个只有服务随机,一个都随机,一个服务时间忽快忽慢。

最慢的那个和最快的那个,平均等待差多少倍?

A 差不到 2 倍。三项主要指标都一样,能差到哪去 B 差 5 到 10 倍 C 差几十倍 D 差无穷倍——最快的那个精确等于 0

五行

走法ca²cs²精确等待p99天花板
D/D/1 都定时000.00000.00100.0%
D/M/1 定时来、随机走011.692710.7480.0%
M/D/1 随机来、定时走102.000010.5680.0%
M/M/1 都随机114.000021.1166.7%
M/H₂/1 忽快忽慢11022.0000140.4826.7%

五行的前三项指标——利用率、吞吐、平均服务时间——完全相同。如果你只有一套标准监控,这五个系统在你的面板上长得一模一样。

而用户体验到的是最后三列。

逐行读

第一行:0。不是「很小」,是零

D/D/1:每 1.25 个时间单位准时来一个,每次服务准时花 1.00。

第一位来了,服务到 1.00,走。第二位在 1.25 来,服务台空着,立刻开始。第三位在 2.50 来,上一位 2.25 就走了。永远不会有第二个人在场。

这台机器忙到 80%,队列长度恒等于 0 或 1。第 11 章在真机上验证过:800 个请求,平均等待 0.0006 毫秒,而那 0.6 微秒是时钟噪声。

这一行的意义是:「高利用率必然导致排队」是错的。利用率本身不产生队。产生队的是利用率和不齐的乘积

第二、三行:不齐从哪来,账单差不多

D/M/1(1.6927)和 M/D/1(2.0000)很接近。一个是「来得准但做得不准」,一个是「来得不准但做得准」。

Kingman 公式对这两行给出同一个答案(都是 2.0000),因为它只看 (ca²+cs²)/2,两行都是 0.5。精确解说明它们其实略有不同——服务的不齐比到达的不齐贵一点点——但同一个量级。

这印证了第 8 章那句话:到达的不齐和服务的不齐地位平等。这在实践上很重要,因为大多数人只想着治理服务(优化慢查询),从没想过治理到达(给定时任务加抖动、给上游做速率整形)。

第四行:4.0000,教科书默认款

M/M/1。到达是泊松、服务是指数。这是所有排队论教材的第一个例子,也是绝大多数「按经验估一下」时脑子里默认的模型。

而第 5 章说过,真实的软件服务,cs² 几乎总是大于 1。所以这一行不是「悲观估计」,它是乐观估计。

第五行:22.0000,也就是你的系统

M/H₂/1,cs²=10。这对应什么?大概是:

  • 大部分请求命中缓存,少部分回源;
  • 大部分查询走索引,少部分全表扫;
  • 大部分用户数据量小,少部分是重度用户。

换句话说,这一行才是绝大多数真实服务的形状。而它的平均等待是 M/M/1 的 5.5 倍,是那个「教科书默认款」的五倍半。

它的 p99 是 140.48 个服务时长。如果一次服务是 10 毫秒,那就是 1.4 秒——在一台忙到 80%、平均处理时间 10 毫秒的机器上。

◆ 最后一列才是这张表的重点

「天花板」那一列回答的是:如果我的要求是「平均等待不超过 2 个服务时长」,我能把机器用到多满?

ρ★ = SLA ÷ (SLA + k × E[S])       k = (ca² + cs²)/2

全定时      k=0     →  ρ★ = 100.0%    机器可以榨干
D/M 或 M/D  k=0.5   →  ρ★ = 80.0%
都随机      k=1     →  ρ★ = 66.7%
忽快忽慢    k=5.5   →  ρ★ = 26.7%     ★ 七成多的产能必须空着

最后一行的意思是:如果你的服务 cs² 是 10,那么你买十台机器,只有不到三台在为吞吐服务,剩下七台是在为「不齐」买单。

反过来说:cs² 从 10 压到 1,你的机器数可以减到 40%——什么代码都不用重写,只要让慢请求别那么慢。这是这本书里性价比最高的一句话。

这些数字是怎么来的

「精确」那一列不是模拟出来的,也不是查表来的,是五个不同的闭式解各自算的:

怎么算的
D/D/1精确的 0。定时来定时走,ρ<1 时永远不排队
M/D/1Pollaczek–Khinchine:ρ/(1−ρ) × (1+0)/2 × E[S]
M/M/1ρ/(1−ρ) × E[S]
M/H₂/1Pollaczek–Khinchine:ρ/(1−ρ) × (1+10)/2 × E[S]
D/M/1没有初等闭式解,要数值解 σ = e^(−(μ/λ)(1−σ)),ρ=0.8 时 σ = 0.628630,然后 Wq = σ/(μ(1−σ))

而 demo 里的「模拟」那一列,是引擎真的一个一个顾客排出来的(每行四十万位顾客,用第 11 章验证过的那行 Lindley 递推)。两条路互相对照——这是这本书唯一能做的「与标准实现对比」,因为排队论没有标准实现。

∑ 算一遍:为什么 D/M/1 没有闭式解

另外四行都能一步写出来,唯独 D/M/1 要解一个超越方程。这个不对称本身很有意思。

原因是 Pollaczek–Khinchine 公式(那个能一步算的)要求到达是泊松的。泊松有一条特殊性质(PASTA,第 7 章提过):到达者看到的系统状态,等于时间平均的系统状态。这条性质让推导可以「站在到达者的角度」直接写下来。

定时到达没有这条性质。定时到达的顾客看到的世界,和随便挑一个时刻看到的世界不一样——他们总是在固定的相位上到达。于是就得老老实实解一个不动点方程:

σ = e^(−(μ/λ)(1−σ))     ← σ 是「来的时候得等」的概率

# ρ=0.8 时用二分法解出 σ = 0.628630
# 也就是说,定时到达的顾客里,62.86% 的人要等;
# 而 M/M/1 里这个数是 ρ = 80%。
# 定时到达让「要等的人」少了 17 个百分点 —— 这就是那 1.6927 vs 4.0000 的来源。

顺便,这也是第 8 章那张「Kingman 在哪里不准」的表的来源:Kingman 对 D/M/1 在低利用率下偏 96.1%,正是因为它用了一个「假装到达是泊松」的近似。

✎ 术语正名:「同样的负载」

「两个系统负载相同」这句话,在这一章之后应该拆成三句:

  1. 吞吐相同(λ 一样);
  2. 利用率相同(ρ 一样);
  3. 不齐程度相同ca²cs² 一样)。

前两条是大家都会说的,第三条几乎没人说——而这一章证明了,只有第三条不同,就足以让两个系统的用户体验差 22 倍

所以下次做容量规划、做压测、做 A/B 对比时,如果只对齐了前两条,那你比较的其实是两个不同的系统。

▸ 在现实里

为什么两个「一模一样」的服务表现不同。同样的代码、同样的机器、同样的 QPS,A 机房比 B 机房慢一倍。查了一圈没发现差异——因为差异在 上:B 机房的流量里混进了一个批量作业,或者某个大客户的请求集中打在了那边。去比一比两边的延迟直方图形状,而不是平均值。

为什么隔离「大客户」那么有效。把大客户的请求路由到独立的池子,这个动作在吞吐上什么也没做——总工作量一点没变。但它把一条 cs²=10 的队,拆成了两条 cs² 都接近 1 的队。按这张表,那是从第五行回到第四行,等待降到 18%。这大概是分级隔离最被低估的收益。

为什么固定大小的批次比动态批次好。动态批次(有多少凑多少)让服务时长变得参差,cs² 上升。固定大小的批次牺牲一点灵活性,把 cs² 压到接近 0。第 20 章会算这笔账。

✗ 这个直觉是错的
这五行是刻意构造的极端案例。真实系统不会差这么多,因为真实系统的分布都在中间某个地方。 真实系统不在中间,它在第五行附近,甚至更右边。第 5 章那个「90% 快 + 10% 慢一百倍」的例子, 是 7.424;而缓存命中率 95% 的服务, 可以轻松超过 20。

被刻意构造的其实是第四行。M/M/1(指数服务时间)是教科书为了数学方便挑的,它在真实软件里几乎不存在——软件的服务时间要么很整齐(固定计算量),要么很不齐(有缓存、有分支、有 IO),很少恰好落在「无记忆」这个特殊点上。

所以这张表的正确读法不是「从 0 到 22 是一个夸张的范围」,而是:你的系统大概在第五行,而你有办法把它挪到第二三行。那是四倍到十倍的改善,不需要加一台机器。

◇ 结算

D差无穷倍——最快的那个精确等于 0。

如果不算第一行(有人会觉得那是耍赖),那么第二行到第五行差 13.0 倍(1.6927 → 22.0000)。选 C 的人也基本对了。

但第一行不是耍赖,它是这一章最重要的一行。它证明的是一个结构性的事实:排队的成因里,「不齐」不是一个修正项,它是一个可以把答案变成零的主因。ρ=0.8 这个数字,在 =0 时对等待没有任何贡献。

A 「三项指标都一样,能差到哪去」——这正是这一章要打的靶子。那三项指标恰好是标准监控面板上有的三项,而它们对这个问题几乎没有信息量。 B 「5 到 10 倍」——这是第四行到第五行的差距(4.0000 → 22.0000 是 5.5 倍)。如果你的直觉停在「随机 vs 很不齐」这个对比上,这个答案是对的。 C 「几十倍」——如果排除掉第一行,这个答案基本正确(13 倍)。而看 p99 那一列的话,从 10.56 到 140.48 也是 13 倍多。

这一章的一句话

同样忙、同样快、同样的吞吐,等待可以从 0 到 22——而这个区间在你的监控面板上是不可见的。

卷 III 结束。你已经完整地看过那面墙了。下一卷问一个很自然的问题:那加机器呢?答案会比你想的复杂三层。第 13 章先从一个所有人每天都在做的选择开始:同样是 4 个窗口、同样的人流,排一条队还是各排各的?答案是 4.57 倍的差距——而且叫号机卖给你的,其实不是公平。