量你自己的队
前二十三章都是我的数字。这一章把工具交给你:三个要量的数、一张诊断表、一台计算器,以及一件几乎所有教程都不会告诉你的事——你量出来的数和公式对不上,多半是正常的。
你在生产环境上量了 3000 个请求,算出平均排队时间是 6.7 毫秒。
而按公式(利用率 80%、平均服务时间 2 毫秒、纯随机),理论值应该是 8.0 毫秒。
差了 16%。这说明什么?
要量的三个数
整本书压缩成一次操作,就是去量这三个数:
| 要量什么 | 怎么量 | 健康区间 |
|---|---|---|
| ① 利用率 ρ | λ × E[S] ÷ 服务台数。注意用请求速率 × 处理时间算,而不是直接看 CPU(CPU 不等于你的瓶颈) | 看第 9 章的 ρ★,取决于 ② 和你的容忍度 |
| ② 不齐 c² | 方差 ÷ 均值²,从你已有的延迟直方图算,一行代码 | < 1 很好,1–4 正常,> 4 值得查 |
| ③ 单次时间 E[S] | p50 延迟(在低负载时测,否则含排队) | 业务定 |
三个数量到了,第 8 章那个公式就能用了,而更重要的是它们指向三条完全不同的排查路线。
发现系统慢的时候,按这个顺序问:
- ρ 是多少?如果 > 0.85——先加机器。第 10 章证明过:等待减半只需要加
1−ρ的产能。在 90% 的地方那是 10%,这是三条路里最便宜、最快、最可预测的一条。 - c² 是多少?如果 > 4——去找那一小撮慢请求。第 5 章证明过:10% 的请求慢 100 倍,贡献了 99.9% 的方差。常见嫌疑人:大 key、冷缓存、N+1 查询、重试、某个大客户。
- 池子有多大?如果服务台数 < 4——先想办法合并池子。第 14 章:同样的利用率,小池子的队长得多。检查有没有不必要的隔离、分片、亲和性绑定。
- 队列有上限吗?如果无界——加上限。第 21 章:
K = 产能 × 能容忍的等待。或者更好,按排队时长丢弃(CoDel 式)。 - 是不是扇出的?如果一个用户请求打到 > 10 个后端——单机的余量要求要按扇出深度倒推。第 16 章:扇出 100 台时,你要的是单机 p99.99。
- 最后才是 E[S]:优化代码、换更快的机器。这是唯一线性的因子,也通常是工程量最大的一条。
大多数团队的默认顺序是反的:先从第 6 条开始。
为什么你量出来的数和公式对不上
这一节是这本书最后一个、也是最重要的诚实交代。
第 11 章我在真机上跑了一条 M/M/1 队列,ρ=0.8,3000 个请求,量出来平均等待 6.6857 毫秒。而公式说是 8.0000 毫秒。
差了 16%。当时我的第一反应是「哪里错了」。
什么都没错。是样本量的问题。
我用模拟器在完全相同的条件下(同样 n=3000,同样 ρ=0.8)跑了 400 次,看看「样本均值」这个统计量本身的分布:
# 稳态真值:8.00 毫秒 # n=3000 时,样本均值的分布: p5 = 6.48 ms p50 = 7.76 ms p95 = 9.79 ms # ★ 也就是说,在这个样本量下, # 你量出 6.5 到 9.8 之间的任何一个数都完全正常。 # 而真机那次量到的 6.6857 落在第 13 个百分位 —— 有点低,但一点也不奇怪。
为什么方差这么大?因为排队时间是高度自相关的。同一个忙期里的所有顾客共享同一段坏运气(第 4 章)。所以你的 3000 个样本里,有效独立样本只有几百个——大致是「忙期的个数」,而忙期平均包含 1/(1−ρ) 个顾客。
1. 不要用短窗口的平均延迟做告警。如果你的告警是「5 分钟平均排队时间超过 X 就报警」,而 5 分钟只有几千个请求,那么你会收到大量假警报——不是系统坏了,是统计噪声。
2. 报告改善时要给区间。「优化后平均延迟从 8ms 降到 6.7ms,改善 16%」——如果样本量是 3000,这个「改善」完全可能是噪声。ρ 越高,需要的样本量越大(有效样本数按 1−ρ 缩小)。
3. 分位数比平均值更稳。p50 的抽样方差比平均值小得多,因为它不受尾巴影响。这是又一个该报中位数而不是平均值的理由(第 4 章给的是另一个理由)。
八件可以明天就做的事
- 把
方差 ÷ 均值²打成一条曲线,挂在利用率旁边。你的监控第一次有能力回答「为什么慢」,而不只是「有多慢」。 - 给所有定时任务加随机抖动。成本几行代码,收益是把 ca² 从「所有人都在整点」压回接近 1。
- 检查每一个无界队列。线程池、消息队列、连接等待队列。给它们设上限,或者按排队时长丢弃。
- 把重试策略从「重试 N 次」改成「重试预算占比」。前者在大面积故障时会把流量放大 N 倍。
- 找出你的 c² 贡献大户。按耗时排序,看最慢的 1% 是什么。它们贡献了大部分排队成本。
- 把「大请求」隔离到独立的池子。不改任何代码逻辑,只改路由,就能把一条 c² 大的队拆成两条小的。
- 压测改成固定到达率模式。「固定并发」模式测的是一个 ca² 很小的假世界。
- 算一下你的服务「能挂几台」:
k < N × (1 − ρ)。很多团队会发现答案是零。
回到你自己的日程表。这本书最私人的一个应用。如果你总觉得「事情永远做不完,而且每件事都拖很久」,去量三个数:你手上同时有几件事在进行(L)、你每周完成几件(λ)、于是每件事平均要多久(W = L/λ)。然后试着把 L 砍一半,什么都不改变。第 2 章那条恒等式保证 W 会跟着减半。
去测你家路由器的缓冲区膨胀。开一个大下载,同时 ping 一个外网地址,看延迟涨多少。如果从 20 毫秒涨到几百毫秒,那就是第 22 章那张表。然后打开路由器的 SQM/fq_codel,再测一次。这是这本书里你今晚就能自己复现的一个结论。
举几个这本书里的结论,它们全都不依赖精确测量:
- 「等待减半需要增加的产能等于当前余量」——这是比例关系,不需要知道等待的绝对值;
- 「ρ 从 90% 到 95%,等待翻倍还多」——同样是比例;
- 「一条队比四条队快 4.57 倍」——比值;
- 「扇出 100 台,p99 会变成 63.4% 的概率」——纯概率,和你的延迟绝对值无关;
- 「Σ ρₖ×Wq,k 是常数」——守恒律,精确成立。
需要精确测量的只有一件事:你现在站在曲线的哪一段。而那只需要知道 ρ 的量级(0.5 还是 0.9),不需要小数点后两位。
C什么都不说明。在 n=3000 的样本上,稳态值 8.00 毫秒的系统,量出 [6.48, 9.79] 之间的任何数都在正常的 90% 区间内。6.7 落在第 13 个百分位。
这道题想教的方法是:看见理论和实测不符时,先问「这个差距在这个样本量下算大吗」,再去怀疑模型或者测量。而回答这个问题的办法很简单——用模拟器在相同条件下跑几百次,看看样本均值本身的分布有多宽。
A 「排队论过于悲观」——这个结论至少要看几十次独立测量的中位数才敢下。而且要小心:Kingman 公式确实系统性偏大(第 8 章那张表),但那是另一回事,与这次的 16% 无关。 B 「测量有问题」——这个怀疑在别的场合是对的(第 2 章说过 Little 定律可以当检查器用),但 16% 的偏差还远远够不上「测量出错」的门槛。 D 「利用率其实没到 80%」——有可能,而且值得顺手核对一下。但同样,16% 的等待偏差只对应 ρ 从 0.80 漂到 0.78,这个量级的漂移在 3000 个请求的样本里也是噪声。这一章的一句话
去量三个数:有多满、有多不齐、一次多久;然后按「先做便宜的」顺序动手。
继续往下走
这本书刻意停在了几个地方,下面是那几扇门。
如果你想把数学补齐
- Mor Harchol-Balter,《Performance Modeling and Design of Computer Systems》(2013)。这是我见过写得最好的排队论教材,而且完全面向计算机系统。第 18 章那些 SRPT 结果就出自她的研究。如果只读一本,读这本。
- Leonard Kleinrock,《Queueing Systems》(两卷,1975–76)。经典,第 19 章那条守恒律就是他的。数学更重,适合当参考书。
- Neil Gunther,《Guerrilla Capacity Planning》。USL 的出处,第 15 章的来源。书名里的「游击」指的是「在没人给你时间和数据的现实里怎么做容量规划」。
如果你想往控制论走(第 22 章那扇门)
这本书讲的是被动的排队。而拥塞控制、自动扩缩容、限流器,都是带反馈的系统,它们有另一套理论。
- Philipp K. Janert,《Feedback Control for Computer Systems》。用工程师听得懂的话讲 PID 和反馈回路,例子全是软件系统。
- Van Jacobson 1988 年那篇《Congestion Avoidance and Control》。互联网没有在 1986 年崩溃,很大程度上因为这篇论文。二十多页,值得读原文。
- Chiu & Jain 1989 关于 AIMD 收敛性的那篇。第 22 章那张收敛图的出处。
- 再往上是控制论本身:Ashby 的必要多样性定律说的是「控制器的多样性必须匹配被控对象的多样性」——而这本书从头到尾在讲的「不齐」,正是那个多样性。这两个学科在这里握手。
如果你想往运筹和精益走(第 23 章那扇门)
- Donald Reinertsen,《The Principles of Product Development Flow》。把排队论直接用在产品研发流程上,我认为是这个方向最好的一本。它有一句话可以当这本书的题词:「你无法管理你没有量化的队列,而大多数组织连队列在哪都不知道。」
- Eliyahu Goldratt,《目标》。用小说讲约束理论。它讲的「瓶颈决定一切」和这本书的「余量决定一切」是一枚硬币的两面。
- 交通流理论:如果第 23 章那条抛物线勾起了你的兴趣,关键词是 fundamental diagram、shockwave theory、以及「三相交通流理论」(Kerner)。
这本书没讲的东西
诚实地列一下缺口,免得你以为自己学完了:
- 排队网络:多个队列串起来(微服务链路就是)。有一套漂亮的理论(Jackson 网络、BCMP),核心结论是「在某些条件下,各个节点可以当成独立的队来算」——但那些条件在现实里经常不成立。
- 重尾的正经处理:第 5 章和第 21 章只是提了一下 c²=∞。真正的重尾排队论(heavy-tailed queueing)是一个活跃的研究领域,结论和本书大不相同。
- 相关到达:重试、推送、雪崩这类「到达之间有强相关」的情况。本书的公式在这里会低估,而这正是线上事故最常见的形态。关键词是 MAP(Markovian Arrival Process)、self-similar traffic。
- 调度理论的正经版本:第 17–19 章只讲了最基本的几个策略。完整的调度理论(包括那些「什么问题是 NP 难的」的结果)是另一本书的量。
《同时》(并发)——那本讲怎么写并发代码,这本讲并发系统为什么会崩。第 15 章的 USL 和那本书里的锁竞争是同一件事的两个视角。
《独占》(操作系统)——CFS 调度器是第 17–19 章那些策略的工业级实现,而 OS 的整个存在意义(让每个进程以为自己独占机器)就建立在「排队和调度」之上。
《意外》(信息论)——那本讲「已经料到的部分一比特都不值」,这本讲「已经料到的部分一秒钟都不用等」。D/D/1 之所以不排队,正是因为它完全可预测——零信息量,零等待。这两本书在 c² 和熵这两个量上是近亲。
《拨动》(因果推断)——第 24 章那个「16% 的偏差算不算异常」的问题,本质上是一个统计推断问题。那本书讲的是怎么从数据里得出可靠结论,这里是一个具体的应用。
最后一句
这本书从「慢,不是因为不够快」开始,到这里可以给出完整版了:
慢是余量不够,而余量之所以不够,一半是因为你用得太满,一半是因为你的活太不齐。你能买到的东西里,速度是最贵的那个,也是效果最小的那个。
而那 30% 空着的机器,从来不是浪费。那是你花钱买下来的、唯一能吸收这个世界的不确定性的东西。