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 只描述「稳态平均」,不含排队 | 结果只是下界估计,实际容量还要减去排队余量 |
| 忽略请求会「重试」 | 重试会让实际到达率翻倍 | λ 要算上重试流量 |
六、本节小结
- L = λ × W:在途请求数 = 到达率 × 驻留时间。单位上就是「每秒几个」乘「几秒」。
- 它可以在压测之前给你资源的量级估计:连接池、线程数、协程并行度。
- 用峰值 QPS × P99 延迟做悲观估计,用平均 × 平均做乐观估计,两者差距往往好几倍。
- 连接池不是越大越好——按 Little’s Law 算出来的 L 就是平均需求量,超出的部分是浪费(甚至会拖慢系统)。
- 它只描述稳态平均,不包含排队效应;所以算出来的数字是乐观的下界,实际还要减掉余量(下一节解释为什么)。
七、自测
- 一个接口峰值 300 QPS、P50 延迟 5 ms、P99 延迟 200 ms。分别用 P50 和 P99 算在途请求数,解释这两个数字在容量规划中的不同用途。
- 数据库连接池设成 200 有什么坏处?(提示:想想 PostgreSQL 每个连接的成本,以及 200 个并发查询对锁竞争和上下文切换的影响)
- 你的服务 QPS 是 500,观测到「活跃请求数」是 100。能不能反推出平均延迟?如果能,是多少?
- 按 P50:
300 × 0.005 = 1.5 个;按 P99:300 × 0.2 = 60 个。P50 的数字告诉你「平时需要多少资源」,P99 的数字告诉你「最坏时刻需要多少资源」。资源按 P50 配 → 尾部必排队;资源按 P99 配 → 平时大量闲置。实践中的做法是取中间值(例如按 P95),并用限流/排队机制保护住极端情况。 - ① 每个连接在数据库侧都有内存/进程开销(PostgreSQL 是每连接一个后端进程),200 个连接可能吃掉可观的内存;② 并发查询数变多会加剧锁竞争并增加上下文切换,某些场景下吞吐反而下降;③ 掩盖了真正的慢查询问题——排队被消除了,但慢的原因还在;④ 一旦流量上涨,压力会成倍转移到数据库。正确做法是先优化查询,再按 Little’s Law 定池大小。
- 能。
L = λW → W = L / λ = 100 / 500 = 0.2 s = 200 ms。所以平均延迟约 200 ms。(这是 Little’s Law 最常用的反推用法。)