文档目录

0.4 用 Little’s Law 估算容量

上一节:0.3 延迟是分布,不是数字 | 下一节:0.5 为什么 90% 利用率已经很危险 配套代码:00-introduction/04-littles-law


一句话结论

「店里同时有多少人」= 「每分钟进来多少人」× 「每个人待多久」。 这个小学除法能让你在动手压测之前就算出:连接池要多大、线程要多少、协程并行度设多少。


一、先说人话:小饭馆的例子

你开了一家小饭馆:

  • 平均每分钟进来 5 个客人(到达率 λ);
  • 每个客人平均坐 20 分钟(停留时间 W)。

那么店里平均同时坐着多少人?

5 人/分钟 × 20 分钟 = 100 人

显然——每分钟进来的人 × 每人待的分钟数 = 店里同时有人数。

这个「显然」的结论,就是 Little’s Law(利特尔法则)。它之所以重要,是因为把「客人」换成「请求」、把「座位」换成「连接」,它就变成了容量估算公式。


二、公式与每个符号的人话含义

L = λ × W
符号 名字 人话 在这个公式里的单位
L 并发数 / 在途请求数 任意时刻系统里「正在被处理、还没结束」的请求个数 个
λ 到达率 每秒有多少个请求进来 req/s
W 驻留时间 一个请求从进来到结束平均花多久 秒

单位换算的直觉:req/s × s = 个——秒被约掉了,剩下的就是「同时有几个」。

⚠️ 最容易犯错的一点:λ 和 W 必须是同一段业务的。用「所有接口的平均 QPS」乘「所有接口的平均延迟」,得到的数字没有意义。要么按接口算,要么按同类资源(比如只算数据库相关请求)算。


三、三个用法(这才是它的价值)

用法 1:算出需要多少资源

问题:你的订单查询接口,峰值 200 QPS,平均延迟 30 ms。数据库连接池要设多大?

λ = 200 req/s
W = 0.030 s
L = 200 × 0.030 = 6 个

结论:平均只需要 6 个连接。

如果连接池设成 100,那 94 个连接平时是闲置的——它们仍然在消耗数据库的内存(PostgreSQL 每个连接都有进程/内存开销)。所以「连接池越大越好」是错的:连接池大小应该接近 L,再留一点余量应对突发。

用法 2:从资源上限反推能扛多少

问题:数据库最多允许 50 个连接,单个查询平均 20 ms。理论最大吞吐是多少?

由 L = λW → λ = L / W = 50 / 0.020 = 2500 req/s

结论:理论天花板 2500 QPS。注意是「理论」——这是在假设 50 个连接全都 100% 忙碌、且没有排队的情况下。现实中因为排队效应(下一节),实际能稳定跑到的远低于这个数字。

用法 3:判断「瓶颈是不是排队」

现象:你的服务显示有 40 个活跃请求,但 QPS 只有 100,延迟 400 ms。

按公式:L 应该是 100 × 0.4 = 40 个  ← 对得上

如果反过来算:

假设服务内部只有 10 个并发处理单元
那么实际并发 40 > 处理能力 10 → 有 30 个在排队

结论:延迟的 300 ms 里,有大部分是排队等待,不是处理时间。 这时候去优化业务代码是白费力气——应该先解决并发度不足的问题(或是搞清楚为什么每个请求要 400 ms)。


四、一个完整的手算例子

场景:一个短链服务的跳转接口(读接口)。

参数 值 来源
峰值 QPS 1000 业务预估
P50 延迟 8 ms 基线测量
P99 延迟 60 ms 基线测量

问题一:平均在途请求有多少?

L = 1000 × 0.008 = 8 个(按 P50 算)

问题二:最坏情况下(P99)在途请求有多少?

L = 1000 × 0.060 = 60 个

这两个数字差 7.5 倍!

这告诉我们:如果把资源按平均值配置(8 个连接),那么在最慢的那 1% 时间窗口里,会有 60 个请求抢 8 个连接 → 严重排队 → 延迟进一步恶化 → 更多请求堆积 → 雪崩。

这就是「按平均值做容量规划」的危险之处。 正确的做法是用峰值 QPS × 尾部延迟来估算上限资源,然后用排队理论(下一节)确定留多少余量。


五、常见误用(每一条都很常见)

误用 为什么错 正确做法
用平均 QPS 算 峰值才是压垮系统的时候 用峰值 QPS(通常留 2–3 倍余量)
用平均延迟算 平均值掩盖长尾 用 P99 延迟做悲观估计,用 P50 做乐观估计,两个都算出来
混着算不同接口 不同接口走不同资源 按资源维度分组(都走数据库的算一组)
算完就当容量上限 Little’s Law 只描述「稳态平均」,不含排队 结果只是下界估计,实际容量还要减去排队余量
忽略请求会「重试」 重试会让实际到达率翻倍 λ 要算上重试流量

六、本节小结

  1. L = λ × W:在途请求数 = 到达率 × 驻留时间。单位上就是「每秒几个」乘「几秒」。
  2. 它可以在压测之前给你资源的量级估计:连接池、线程数、协程并行度。
  3. 用峰值 QPS × P99 延迟做悲观估计,用平均 × 平均做乐观估计,两者差距往往好几倍。
  4. 连接池不是越大越好——按 Little’s Law 算出来的 L 就是平均需求量,超出的部分是浪费(甚至会拖慢系统)。
  5. 它只描述稳态平均,不包含排队效应;所以算出来的数字是乐观的下界,实际还要减掉余量(下一节解释为什么)。

七、自测

  1. 一个接口峰值 300 QPS、P50 延迟 5 ms、P99 延迟 200 ms。分别用 P50 和 P99 算在途请求数,解释这两个数字在容量规划中的不同用途。
  2. 数据库连接池设成 200 有什么坏处?(提示:想想 PostgreSQL 每个连接的成本,以及 200 个并发查询对锁竞争和上下文切换的影响)
  3. 你的服务 QPS 是 500,观测到「活跃请求数」是 100。能不能反推出平均延迟?如果能,是多少?
  1. 按 P50:300 × 0.005 = 1.5 个;按 P99:300 × 0.2 = 60 个。P50 的数字告诉你「平时需要多少资源」,P99 的数字告诉你「最坏时刻需要多少资源」。资源按 P50 配 → 尾部必排队;资源按 P99 配 → 平时大量闲置。实践中的做法是取中间值(例如按 P95),并用限流/排队机制保护住极端情况。
  2. ① 每个连接在数据库侧都有内存/进程开销(PostgreSQL 是每连接一个后端进程),200 个连接可能吃掉可观的内存;② 并发查询数变多会加剧锁竞争并增加上下文切换,某些场景下吞吐反而下降;③ 掩盖了真正的慢查询问题——排队被消除了,但慢的原因还在;④ 一旦流量上涨,压力会成倍转移到数据库。正确做法是先优化查询,再按 Little’s Law 定池大小。
  3. 能。L = λW → W = L / λ = 100 / 500 = 0.2 s = 200 ms。所以平均延迟约 200 ms。(这是 Little’s Law 最常用的反推用法。)