台子越多,越经得起挤
「利用率 90%」这句话对一台机器和对一百台机器,含义完全不同。同样的拥挤程度,大池子排出来的队短得多——而这条规律,是整个云计算商业模式的数学基础。
两个系统,利用率都是 90%,请求的分布也完全一样。区别只有池子大小:
- 系统甲:1 个服务台,来的活也少(负载按比例缩小);
- 系统乙:64 个服务台,一条共享队列。
两边的拥挤程度完全相同。平均等待差多少?
同样的 90%,不同的队
| 台子数 | 利用率 | 平均等待 | 来了得等的概率 |
|---|---|---|---|
| 1 | 90% | 9.0000 | 90.0% |
| 4 | 90% | 1.9694 | 78.8% |
| 16 | 90% | 0.3696 | 59.1% |
| 64 | 90% | 0.0485 | 31.1% |
| 128 | 90% | 0.0132 | 16.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%,体验也更好。这是这条规律在日常工作里最直接的一个应用。
算一下。ρ=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%。