卷 IV · 加人CH 14深度 14/24

台子越多,越经得起挤

「利用率 90%」这句话对一台机器和对一百台机器,含义完全不同。同样的拥挤程度,大池子排出来的队短得多——而这条规律,是整个云计算商业模式的数学基础。

规模经济Erlang C墙往右搬

▷ 先估一个数

两个系统,利用率都是 90%,请求的分布也完全一样。区别只有池子大小:

  • 系统甲:1 个服务台,来的活也少(负载按比例缩小);
  • 系统乙:64 个服务台,一条共享队列。

两边的拥挤程度完全相同。平均等待差多少?

A 一样。利用率相同,等待就该相同 B 大池子快几倍 C 大池子快几十倍 D 大池子快一百倍以上

同样的 90%,不同的队

台子数利用率平均等待来了得等的概率
190%9.000090.0%
490%1.969478.8%
1690%0.369659.1%
6490%0.048531.1%
12890%0.013216.9%

五行的利用率完全相同。都是 90%,都只剩 10% 余量。

而平均等待从 9.0000 掉到 0.0132 个服务时长——快了 680 倍

最后一列更直白:1 个台子时,90% 的人来了得等;128 个台子时,83.1% 的人一来就有台子空着,直接开始。

为什么

机制和第 13 章是同一个,只是推到了极端:大池子里,「全部台子同时都忙」这件事变得很罕见。

粗略地想:如果每个台子独立地有 90% 概率在忙,那么:

# (这只是直觉性的算法,真实的 Erlang C 里各台子并不独立,
#   但它给出的量级是对的)

1 个台子全忙的概率     = 0.9              = 90%
4 个台子全忙的概率     ≈ 0.9⁴  = 0.656    = 66%
16 个台子全忙的概率    ≈ 0.9¹⁶ = 0.185    = 19%
64 个台子全忙的概率    ≈ 0.9⁶⁴ = 0.00121  = 0.12%

# 只有"全忙"的时候才会有人真的等。
# 台子越多,这个状态越罕见 —— 而它是**指数级**罕见的。

这就是规模经济在排队里的形式。它和「买得多便宜」那种规模经济不是一回事——这里的收益不来自采购议价,而来自统计上的平滑:很多个独立的波动叠在一起,相对波动会缩小(按 1/√n)。

◆ 换个方向读:墙往右搬了

上面那张表固定利用率、看等待。反过来固定等待、看利用率,得到的是这本书里最有商业意味的一张表:

# 要求:平均等待不超过 0.5 个服务时长
# 问:我能把机器用到多满?

1 个台子    →  ρ★ = 33.3%      ← 三分之二的产能必须空着
4 个台子    →  ρ★ = 74.7%
16 个台子   →  ρ★ = 91.8%
64 个台子   →  ρ★ = 97.6%
128 个台子  →  ρ★ = 98.7%      ← 只需要空着 1.3%

第 9 章说过,那面墙永远在 ρ=100%,而不齐会把交点往左搬。这一章给出了往搬的办法:把池子做大。

而这正是云计算在卖的东西。你买的不是更快的 CPU——公有云的单核性能通常还不如你自己的服务器。你买的是把你的波动扔进一个几万台的池子里,让它和别人的波动互相抵消。

这条规律的三个用法

1. 合并小池子。如果你有五个服务,各自跑在自己的 4 台机器上、各自 60% 利用率——把它们合并成一个 20 台的池子,可以在同样的延迟下跑到 85% 以上,也就是省掉四分之一的机器。这就是容器编排和「混部」的收益来源。

2. 警惕切碎池子的操作。反过来,很多常见操作在偷偷把池子切小:

  • 按可用区隔离:3 个 AZ = 3 个池子,每个只有三分之一大;
  • 按租户隔离:每个大客户一个独立池;
  • 按分片路由:一致性哈希把请求绑死在特定实例上;
  • 金丝雀发布:临时切出一个小池子接 5% 的流量。

这些都有正当理由(故障隔离、噪声邻居、灰度安全),但它们都在付规模经济的代价,而且代价在高利用率下很大。金丝雀那个尤其容易被忽略:那 5% 的流量打到一两台机器上,它的延迟表现天然就该比主池子差——而团队经常把这个差异误判成「新版本有性能问题」。

3. 小池子必须留更多余量。这条最实用。如果你的服务只有 3 台机器,那么「70% 是安全水位」这种通用经验完全不适用——3 台机器在 70% 上排的队,比 100 台机器在 90% 上排的队还长。池子越小,安全水位越低。

∑ 算一遍:平方根员工法则

运营研究里有一条广为流传的经验法则,叫 square root staffing rule

# 要处理 a 个爱尔兰的负载(a = λ/μ,也就是"平均同时有几个活在做"),
# 需要的服务台数量大约是:

c ≈ a + β√a          β 通常取 0.5 到 2,取决于你要多好的服务水平

# 换句话说:
#   基础需求 a  ——  这部分是干活用的
#   额外的 β√a  ——  这部分是买余量用的  ★

# 例子(β=1):
a = 1    →  c = 2      需要 100% 的额外产能
a = 4    →  c = 6      需要 50%
a = 100  →  c = 110    需要 10%
a = 10000→  c = 10100  需要 1%

看最后一列:需要的额外产能比例,按 1/√a 下降。

这条法则把「大池子好」量化成了一句可操作的话:余量的绝对量按 √负载 增长,所以余量的比例按 1/√负载 缩小。它也解释了为什么呼叫中心行业能算得那么精——Erlang 一百多年前推导这套公式,就是为了回答「这个交换局要装几条线」。

✎ 术语正名:「弹性」

云的宣传里,「弹性」通常指能快速扩缩容。但这一章说明它还有一层更基础的含义:

弹性 = 你的波动被扔进了一个大得多的池子里。

即使你完全不做自动扩缩容,把服务从自建机房搬到公有云,你也享受到了一部分规模经济——因为底层的物理资源池是共享的,你的峰值和别人的谷值互相抵消。这部分收益是静态的,不需要任何弹性伸缩配置。

反过来说,如果你在云上买了预留实例、绑定了专用宿主机、做了严格的资源隔离——你把这部分收益退回去了。隔离和规模经济是一对天然的对头,而大多数架构决策是在这两者之间选位置。

▸ 在现实里

为什么大医院的急诊比小医院快(在同样拥挤的情况下)。20 张床的急诊科和 5 张床的,同样 90% 占床率,前者的等待时间要短得多。这是医疗资源规划里「区域集中 vs 就近分散」争论的数学核心——集中提高效率,分散降低到达时间,而最优解取决于路程时间和排队时间哪个大。

为什么保险公司要做大。保险的本质是把很多人的风险汇集起来,让相对波动按 1/√n 缩小。这和排队的规模经济是同一个数学,只是一个作用在赔付金额上,一个作用在等待时间上。

为什么「一人一机」的开发环境那么浪费。每个工程师一台专属构建机,利用率通常不到 10%,但构建还是要等。因为那是 c=1 的池子——它的等待表现极差。共享的构建集群哪怕跑到 80%,体验也更好。这是这条规律在日常工作里最直接的一个应用。

✗ 这个直觉是错的
把 10 台机器的池子拆成两个 5 台的(做故障隔离),只要每边的流量也减半,利用率不变,性能就不受影响。 利用率确实不变,但等待时间会明显变长。拆池子是有代价的,而这个代价在高利用率下很大。

算一下。ρ=90%,一次服务 1 个时间单位:

10 台一个池子   Wq ≈ 0.6931
5 台两个池子    Wq ≈ 1.4436       ← 慢一倍多

故障隔离当然值这个价——一个池子挂掉只影响一半用户,这个收益是实打实的。但它应该被当成一笔交易来讨论,而不是一个免费的好习惯。

更常见的错误版本是「隔离之后要不要多加机器」。答案是:拆池子之后,为了维持同样的延迟,每个小池子的目标利用率必须调低。而这一点几乎从来没有出现在架构评审的检查单上。

◇ 结算

D快一百倍以上。1 个台子 9.0000,64 个台子 0.0485——快了 186 倍

如果比到 128 个台子(0.0132),是 682 倍

这个数字之所以大得离谱,是因为规模经济是指数级的(「全部台子同时忙」的概率按台数指数衰减),而我们的直觉默认它是线性的。

A 「利用率相同,等待就该相同」——这是把利用率当成了唯一的状态变量。它是第 8 章那三个因子里的一个,但公式里还有一项被这一章暴露出来了:台子数量。严格说,多台子的公式是 Erlang C,不是 ρ/(1−ρ) B 「快几倍」——这大概是从第 13 章那个 4.57 倍推过来的。但第 13 章比的是「一条队 vs 四条队」,这一章比的是「小池子 vs 大池子」,后者的收益大得多。 C 「快几十倍」——量级接近,但仍然低估。1 vs 16 是 24 倍,1 vs 64 就到 186 倍了。

这一章的一句话

同样的拥挤程度,池子越大队越短;云卖的不是更快的机器,是一个大得多的池子。

可是加机器不能一直加下去。下一章讲反面:有一类系统,加人到某个点之后,总吞吐会开始下降——不是收益递减,是收益为负。一份实测数据里,64 个并发跑出 18207 请求/秒,而线性外推会以为能跑 75520;再往上加到 128,吞吐反而比峰值还少 10.2%