三个因子,相乘
如果这本书只能留下一行字,就是这一行。它把前七章讲的所有东西压成三个相乘的因子——而你会立刻看出,日常那些「让系统变快」的动作,全都在打其中一个,而且往往是最贵的那一个。
你的服务:利用率 80%,到达和服务都是纯随机的(ca² = cs² = 1),平均每次处理 10 毫秒,平均等待 40 毫秒。
老板给你一笔预算,只够做一件事。三个选项,效果分别是:
- 换更快的机器:单次处理从 10 毫秒降到 5 毫秒。
- 加机器降负载:利用率从 80% 降到 70%。
- 治理慢请求:把服务的 cs² 从 1 降到 0。
哪个选项把平均等待压得最低?
那一行
Wq ≈ ρ/(1−ρ) × (ca² + cs²)/2 × E[S]
─────────── ───────────────── ──────
满的程度 不齐的程度 一次多久
这是 Kingman 公式(1961 年,John Kingman)。三个因子,相乘。
把它们一个一个拆开:
① 满的程度:ρ/(1−ρ)
分母是 1 − ρ,也就是余量。余量在分母上,意味着余量减半,这个因子翻倍还多。
这是三个因子里唯一一个非线性的,也是唯一一个没有上界的。ρ=0.5 时它是 1,ρ=0.9 时它是 9,ρ=0.99 时它是 99。下一卷整整四章都在讲它。
② 不齐的程度:(ca² + cs²)/2
到达的不齐和服务的不齐,各占一半,地位完全平等。
这一项是第 5 章那个 c² 的直接应用,而它之所以长这个样子(一个平均),来自第 7 章的检查悖论——你要为「更容易撞上长的那个」买单,来源有两个:到达扎堆,或者服务时长参差。
注意:它也没有上界。而且第 5 章说过,真实系统的 cs² 经常远大于 1。
③ 一次要多久:E[S]
这是唯一一个「速度」项,也是唯一一个线性的。机器快一倍,它减半,就这样。
而且它有个隐藏的连带效应:如果你在不改负载的前提下让机器快一倍,ρ 也会跟着减半,于是因子 ① 也会变小。这是「换更快的机器」真正的价值所在——它同时打两个因子。这个细节在结算里会算清楚。
不要把它当成一个「算等待时间」的工具——它是近似的,算出来的数别太当真。把它当成一张诊断表。
任何时候有人说「系统变慢了」,你可以问:是哪个因子变大了?
- ① 变大 → 流量涨了,或者产能掉了。看利用率曲线。
- ② 变大 → 出现了一批异常慢的请求,或者流量开始扎堆。看延迟直方图的形状,看 c²。
- ③ 变大 → 单次处理真的变慢了。看 p50,看火焰图。
三个因子对应三条完全不同的排查路线。而绝大多数人只会走第三条——因为那是唯一一条有工具的。
它在哪里精确,在哪里不准
Kingman 公式是近似的,而一本教你思考的书有义务告诉你近似在哪里会骗你。好消息是:它失灵的位置很规律。
| 情形 | Kingman | 精确解 | 偏差 |
|---|---|---|---|
| M/M/1,ρ=0.8(到达服务都随机) | 4.0000 | 4.0000 | +0.0% |
| M/D/1,ρ=0.8(随机来、定时走) | 2.0000 | 2.0000 | +0.0% |
| D/M/1,ρ=0.5(定时来、随机走) | 0.5000 | 0.2550 | +96.1% |
| D/M/1,ρ=0.8 | 2.0000 | 1.6927 | +18.2% |
| D/M/1,ρ=0.95 | 9.5000 | 9.1724 | +3.6% |
| D/M/1,ρ=0.99 | 49.5000 | 49.1678 | +0.7% |
三条规律,都很好记:
- 只要到达是泊松的(ca²=1),它就是精确的。此时它退化成 Pollaczek–Khinchine 公式,那是个精确定理,不是近似。而第 6 章说过,大量独立用户叠加就会收敛到泊松——所以在真实的 Web 服务里,这个公式基本上是精确的。
- ρ 越接近 1,它越准。D/M/1 在 ρ=0.5 时偏 96%,到 ρ=0.99 时只偏 0.7%。Kingman 本来就是作为重流量极限推导出来的——它在墙脚下最准,而墙脚下恰好是你最关心的地方。
- 它偏大,不偏小。在上面所有例子里,Kingman 给出的都是上界。作为一个用来做容量规划的工具,「宁可高估」是正确的方向。
那个 D/M/1 的精确解不是查表来的,是引擎数值解出来的:σ 要满足 σ = e^(−(μ/λ)(1−σ)),ρ=0.8 时 σ = 0.628630,然后 Wq = σ / (μ(1−σ))。第 12 章会拿它做招牌 demo 的一行。
公式不是从天上掉下来的。它可以从第 7 章那个「剩余服务时间」推出来:
# 你到达时,要等两样东西: # ① 队里已经排着的那些人,全部做完 # ② 加上正在被服务的那位剩余的时间 Wq = (队里的人数) × E[S] + P(有人在被服务) × E[剩余] # 用 Little 定律把「队里的人数」换成 λ × Wq(第 2 章那条恒等式) Wq = λ Wq E[S] + ρ × E[S](1 + cs²)/2 Wq = ρ Wq + ρ E[S](1 + cs²)/2 ← 因为 λE[S] = ρ Wq (1 − ρ) = ρ E[S](1 + cs²)/2 Wq = ρ/(1−ρ) × (1 + cs²)/2 × E[S] ★ 这就是 P-K 公式(精确) # Kingman 做的唯一一步近似:把 (1+cs²)/2 换成 (ca²+cs²)/2 # 也就是「到达的不齐,和服务的不齐,同等对待」。 # ca²=1 时两者相同 —— 所以泊松到达下它是精确的。
推导里那一步「用 Little 定律换掉队长」值得停一下:第 2 章那条看起来什么都没说的恒等式,在这里是整个推导的关键。这是它作为「不需要假设」的定律的价值——它可以插进任何一个推导里。
上面表格里那些 M/M/1、M/D/1 是排队论的标准写法,叫肯德尔记号。三段,用斜杠隔开:
A / B / c │ │ └── 服务台数量 │ └────── 服务时间的分布 └────────── 到达间隔的分布 # 字母表: M = Markovian / Memoryless,指数分布,c² = 1(M 是「无记忆」的意思) D = Deterministic,定时,c² = 0 G = General,任意分布(只知道均值和 c²) Ek = Erlang-k,c² = 1/k H2 = 两相超指数,c² > 1 # 所以: M/M/1 随机来、随机走、一个台子 ← 教科书的默认款 M/D/1 随机来、定时走、一个台子 ← 固定长度的包 D/M/1 定时来、随机走、一个台子 ← 定时任务打到一个变长的处理器上 M/G/c 随机来、任意分布、c 个台子 ← 最接近真实服务的模型
这套记号 1953 年由 David Kendall 提出。它有用的地方在于:它逼你显式回答「到达齐不齐」和「服务齐不齐」这两个问题,而这正是大多数性能讨论里被跳过的两个问题。
把它当成一次事故复盘的框架。下次线上变慢,别直接跳进火焰图。先在白板上写三个因子,逐个问:利用率从多少变成了多少?延迟直方图的形状变了吗?p50 变了吗?这三个问题能在五分钟内把排查范围缩小到三分之一。
为什么「灰度发布变慢了」经常不是新代码的锅。灰度期间,一部分流量走新版本、一部分走旧版本,两个池子各自的规模都变小了——而池子变小会让同样的利用率产生更长的队(第 14 章)。因子 ① 变大了,而代码一行没动。
为什么加缓存有时候让 p99 更糟。加缓存降低了平均处理时间(因子 ③ 变小),但把请求分成了「命中,1 毫秒」和「未命中,100 毫秒」两群——因子 ② 暴涨。两个因子方向相反,净效果可能是负的。这在真实系统里屡见不鲜,而只看平均延迟的监控完全看不出来。
更要命的是,优化单次耗时通常是最贵的——它意味着重写代码、换技术栈、买更好的硬件,工程量以人月计。而:
- 降低 ρ(因子 ①)通常只要加机器,是花钱就能买到的,而且第 10 章会证明在高利用率下便宜得离谱;
- 降低 c²(因子 ②)通常是找出那一小撮慢请求,是局部改动,而且第 5 章算过收益是平方级的。
这一章真正想让你带走的不是公式,是这个排序:先看满不满,再看齐不齐,最后才动速度。大多数团队的顺序恰好是反的。
C治理慢请求。把这三个选项都代进公式:
# 原始:ρ=0.8, (ca²+cs²)/2 = 1, E[S] = 10ms Wq = 4.0 × 1.0 × 10 = 40 ms # ① 换更快的机器:E[S] 10→5ms。注意 ρ 也会跟着 0.8→0.4 Wq = (0.4/0.6) × 1.0 × 5 = 0.667 × 5 = 3.33 ms ← 降到 8.3% # ② 降负载:ρ 0.8→0.7 Wq = (0.7/0.3) × 1.0 × 10 = 2.333 × 10 = 23.3 ms ← 降到 58% # ③ 治理慢请求:cs² 1→0,所以 (1+0)/2 = 0.5 Wq = 4.0 × 0.5 × 10 = 20 ms ← 降到 50%
咦?① 才是最好的?
是的——但请注意为什么:因为「机器快一倍」在负载不变时同时打了两个因子(E[S] 减半,ρ 也减半)。它赢不是因为速度重要,而是因为它偷偷降了利用率。
如果把这个偷来的好处还回去(比如你换了更快的机器之后立刻把流量也加倍,利用率仍然是 80%——真实世界里这几乎必然发生),那么:
# ①' 换更快的机器,但流量也跟着涨,ρ 保持 0.8 Wq = 4.0 × 1.0 × 5 = 20 ms ← 和 ③ 完全一样
所以答案是 C:在控制利用率不变的对比下,治理慢请求和把机器换快一倍效果相同——而后者要花的钱多一个数量级。
这道题真正想教的是:算收益的时候,一定要说清楚「其他条件是否不变」。「换更快的机器」这个动作的收益里,大部分来自它顺手降低的利用率,而那部分收益你本来可以直接花更少的钱买到。
A 「最直接」——它在这道题的字面设定下确实最优,但赢的原因不是速度,是它连带降了 ρ。而这个连带效应在生产环境里通常活不过一个季度。 B 「加机器降负载」——80% 降到 70% 只让因子 ① 从 4.0 降到 2.33。在 80% 这个位置上,10 个百分点还买不到「减半」——第 10 章会给出精确的价目表。 D 「差不多,选便宜的」——从结果上看 ③ 和 ①' 确实差不多,但 ② 明显差一档。而且三者的成本差一个数量级,「选便宜的」在这里恰好会选中最优解。这一章的一句话
等待 = 满 × 不齐 × 一次多久;三个因子里只有一个是速度,而它是唯一线性的那个。
卷 II 结束。你现在有了公式的完整形状。下一卷只做一件事:把第一个因子 ρ/(1−ρ) 画出来,反复地看。它是一条你在中学见过、但从来没在工作里认出来的曲线——从 50% 到 90%,等待涨 9.0 倍;从 90% 再到 95%,只多用了 5 个百分点,等待再涨 2.1 倍。