大促前一周的容量评审会上,有人问了一句:我们这套系统到底能扛多少 QPS?三个人给出了三个答案——8 万、15 万、30 万。三个数字都不是随口说的:一个来自上次压测的峰值,一个来自去年大促实际峰值乘了个系数,一个来自架构设计文档里写的目标值。三个答案都对,又都没法直接用。因为下一个问题没人答得上来:如果明天流量真的翻倍,先撑不住的到底是哪一层?
容量预测做到这个份上,问题已经不在数学,而在三套互不相通的经验在打架。很多人对容量预测的想象是:跑一次压测,拿到一个数字,然后照着这个数字扩机器。真正在高并发系统里待过几轮的人会知道,压测给的是某个时刻、某个版本、某种流量结构下的一次快照,而线上流量是活的,代码是变的,依赖是会长胖的。你用一个快照去回答一个持续变化的问题,误差是必然的,区别只在于这个误差是可控的,还是要命的。
我更愿意把容量预测理解成一次“有限信息下的决策”:你手上永远没有完整数据,但你得在扩容窗口关闭之前,给出一个能承担后果的数字,同时说清楚这个数字在什么条件下成立。从这个角度看,容量预测的核心产出不是一个 QPS 数值,而是一份带有假设、边界和回退方案的判断。经验有用,但经验不可复用、不可审计、不可传递;数据也有用,但数据不给判断。真正要做的,是让两者互相校准。
下面的内容会沿着这条线展开:先讲清楚经验预测为什么会系统性偏,再讲怎么把“能扛多少”拆成可测量的量,然后讲数据从哪来、怎么用,最后落到一个可以长期运营的机制上。
1. 为什么“能扛多少 QPS”这个问题,资深工程师也常常答不准
这不是能力问题,而是问题本身的形态问题。一个系统在 40% 负载下的表现,和在 85% 负载下的表现,不是同一类现象。前者是“资源还有富余”,后者是“排队已经开始,任何一点抖动都会被放大”。如果你把这两个时刻的数据混在一起谈,得到的结论一定是模糊的。
1.1 容量是一个区间,不是一个数字
系统的负载承受能力,通常可以切成三段来看。第一段是舒适区,CPU、内存、连接数都在安全范围内,延迟曲线平缓;第二段是拐点区,资源接近饱和,排队开始出现,延迟对负载变得敏感;第三段是崩溃区,请求开始超时重试,重试又带来更多请求,形成正反馈。
这三段的分界点不是固定的,它随流量结构、数据量、依赖状态而变化。所以更实用的表达方式不是“我们能扛 10 万 QPS”,而是“在平均响应体 2KB、读写比 9:1、缓存命中率 95% 的假设下,我们能扛 10 万 QPS,超过 8 万时 p99 延迟会明显上升”。
| 负载水位 | 典型现象 | 可采取的动作 |
|---|---|---|
| 40%~60% | 资源富余,延迟平稳 | 可作为日常运行区间 |
| 60%~75% | 延迟开始小幅波动 | 监控队列长度和 GC 频率 |
| 75%~85% | 排队显现,p99 抬升 | 触发扩容预警,准备预案 |
| 85% 以上 | 超时与重试互相放大 | 限流、降级,避免进入崩溃区 |
这张表的意义在于,它把“容量”从一个点变成了一个带上下文的区间。你在评审会上报数字的时候,如果只报一个点,别人没法判断风险;如果报一个区间加上假设条件,讨论才有可能继续下去。
这里有个容易被忽略的细节:拐点位置会随着数据量增长而前移。同样一套服务,上线三个月后表数据涨了五倍,缓存命中率掉了两个百分点,拐点可能就从 80% 挪到了 72%。这也是为什么“去年这个配置扛住了”这句话经常失效——配置没变,但系统已经变了。
1.2 经验预测的三类系统性偏差
经验不是没价值,经验的价值在于它包含了大量数据里看不到的上下文。但经验在容量预测这个场景里,会稳定地产生三类偏差。
第一类是锚定偏差。人一旦拿到一个参考数字,后续判断就会围绕它摆动。上次压测跑到 12 万 QPS,这次评估就会不自觉地向 12 万靠拢,哪怕业务模型已经完全变了。更麻烦的是,这个锚点往往来自一次“成功”的压测,而那次压测的流量模型可能是理想化的。
第二类是幸存者偏差。团队记住的往往是“那次大促我们扛住了”,但很少有人系统记录“那次我们差点没扛住,是因为哪个环节”。没有记录,就没有校准。下一次评估时,你还是用同一个乐观假设,只是运气可能不一样了。
第三类是线性外推偏差。流量增长看起来是线性的,但容量消耗往往不是。用户量翻倍,如果伴随的是访问模式变化——比如从浏览为主变成抢购为主,从单条查询变成批量查询——那么资源消耗可能是超线性的。数据库连接数、缓存带宽、锁竞争,这些指标在拐点之后的表现都不线性。
一个实用的纠偏做法是:每次容量评估,强制写出三条“如果这个假设不成立,结论会怎么变”。写不出来,说明这次评估其实没有假设,只有感觉。
除了这三类偏差,还有一个结构性问题是压测环境与生产环境不一致。机器规格、网络拓扑、依赖服务的真实延迟、数据规模,任何一项有差异,压测结果就只能作为下限参考,而不是结论。这一点在后面的压测章节会展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 把容量拆成可测量的量:从“感觉能扛”到“这条链路能扛”
“系统能扛多少”是一个过于聚合的问题,聚合到无法回答。能回答的问题形态是:“这条链路上,第几跳会先饱和,饱和时它的表现是什么”。所以容量预测的第一步不是算,而是拆。
2.1 先画清楚请求链路,再谈容量
以一个典型的下单请求为例,它可能依次经过接入层、网关、业务服务、库存服务、订单服务、支付前置、消息队列,最后落库。每一跳都有自己的容量上限,整条链路的容量取决于最窄的那一段。
这里有个常见的误判:大家习惯盯着业务服务看,因为它的 QPS 指标最直观。但实际先出问题的经常是那些“看起来不处理业务”的环节——数据库连接池被打满、缓存集群带宽跑满、注册中心心跳把某个节点压垮、日志采集把磁盘 IO 占满。
拆链路的时候,建议按这个顺序记录每一跳的关键约束:
- 接入层:连接数上限、TLS 握手开销、单机带宽
- 网关:路由规则数量、鉴权调用耗时、限流阈值
- 业务服务:线程池大小、GC 停顿、下游超时配置
- 数据库:连接池上限、慢查询比例、主从延迟
- 缓存:命中率、单 key 热点、集群分片倾斜
- 消息队列:分区数、消费组并发、积压告警阈值
把这些约束列出来之后,你会发现“容量”其实是若干个具体数字的最小值,而不是一个抽象的总量。这个过程本身就有价值,因为它把讨论从“够不够”变成了“哪一段不够”。
2.2 单机容量、集群容量与共享资源
单机压测出来的数字,不能直接乘以机器数。原因有三个。
一是共享资源的争抢。机器数量增加,对数据库、缓存、消息队列的压力是同步增加的。如果这些下游没有跟着扩容,集群整体容量会在某个点之后不再增长,甚至因为连接数过多而下降。
二是负载均衡的均匀度。理论上 N 台机器能扛 N 倍,实际因为长连接分布、请求耗时差异、实例性能差异,总有一批机器先到瓶颈。均匀度差 10%,实际可用容量就少 10%。
三是集群内部的协调开销。节点数变多之后,选主、心跳、配置同步这些后台动作的绝对开销会上升,这部分不会体现在单机压测里。
所以在做容量换算时,我通常会用两层系数:一层是单机到集群的线性度系数,通常在 0.75 到 0.9 之间,具体取决于负载均衡策略和下游瓶颈;另一层是共享资源的余量系数,要求下游容量至少是上游目标容量的 1.3 倍以上。
python复制# 集群容量粗算示例,仅示意换算逻辑,参数需按实际环境标定
single_node_qps = 1800 # 单机压测在目标延迟下的稳定值
node_count = 24 # 可用实例数
linear_factor = 0.85 # 单机到集群的线性度,来自历史回归
downstream_headroom = 0.75 # 下游最多只能承受 75% 的容量占用
cluster_capacity = single_node_qps * node_count * linear_factor
safe_capacity = cluster_capacity * downstream_headroom
print(f"理论集群容量: {cluster_capacity:.0f} QPS")
print(f"考虑下游余量后的安全容量: {safe_capacity:.0f} QPS")
这段代码的目的不是给出精确答案,而是把假设显式化。当有人质疑结果时,你可以逐项讨论系数,而不是争论一个孤零零的数字。
2.3 一条针对容量异常的排查链路
容量评估做完,上线后指标不对,这时候需要一条排查顺序,而不是随机翻监控。
- 先看现象:是延迟上升、错误率上升,还是吞吐上不去但资源没满。三者指向完全不同的问题。
- 再看流量结构:QPS 总量没变,但读写比、请求体大小、热点分布是否变了。
- 再看单机指标:CPU、内存、线程池活跃数、GC 停顿,找出最先异常的那个。
- 再看下游:数据库慢查询、缓存命中率、依赖服务 p99,确认是不是被下游拖住。
- 最后看配置与版本:线程池参数、超时配置、连接池大小,以及最近一次发布改了什么。
这个顺序的价值在于它遵循“先定位层次,再定位原因”的原则。跳过前面几步直接怀疑代码,往往会浪费大量时间。
3. 数据从哪来:三类必须长期保留的观测数据
说“数据驱动容量预测”很容易,难的是这些数据从哪来、怎么保证它可信、怎么保证半年后还能查到。容量预测最尴尬的场景是:大促前需要复盘去年同类场景的容量消耗,结果发现当时的监控数据只保留了 7 天。
3.1 压测数据:怎么测才不至于自欺欺人
压测是容量预测里最容易被做形式化的环节。常见的无效压测有三种:流量模型不对、数据规模不对、观测点不对。
流量模型不对,指的是压测只压了一个接口的均匀请求,而线上是混合接口、有缓存命中差异、有明显的冷热分布。这种压测得到的数字通常偏乐观,因为它忽略了真实请求的多样性。
数据规模不对,指的是压测库只有几十万条数据,而线上是几千万条。索引深度、查询计划、缓存淘汰行为都会不同。这类差异会让压测结果高估容量。
观测点不对,指的是只看平均延迟,不看 p99、不看 GC、不看队列长度。平均值会把拐点抹平,让你误以为还有很大余量。
一次有价值的压测,至少要做到这几点:
- 用生产流量的采样回放,而不是人工构造的均匀请求
- 数据量级尽量接近生产,至少覆盖主要查询路径的数据分布
- 阶梯加压,每档稳定运行足够长时间,观察延迟曲线的拐点
- 同时记录单机资源和下游依赖的指标
- 记录压测时的版本号、配置、数据规模和流量模型,作为后续对比的基准
压测结果如果不带版本号和流量模型描述,半年后基本没有参考价值。它只是一个曾经出现过的数字。
阶梯加压时,建议每档至少跑 10 到 15 分钟。时间太短,GC 周期、缓存预热、连接池建立这些慢变量还没进入稳定状态,你看到的是系统在“热身”,不是真实表现。
3.2 生产流量数据:最贵的样本
压测是主动构造的样本,生产流量是被动得到的样本。后者更真实,但需要长期保存和结构化处理。
需要长期保留的几类数据:按接口维度的 QPS 与延迟分位、按资源维度的利用率、按依赖维度的调用耗时与错误率、以及这些指标的日/周/月峰值形态。峰值形态特别重要,因为它决定了你需要为“最坏的那一小时”准备多少余量。
存储策略上,建议分两层:原始指标保留 15 到 30 天,用于精确排查;聚合指标(小时级、天级)保留一年以上,用于趋势分析和同比参照。很多团队只做了前者,结果做年度容量规划时手上只有几天的数据。
处理这些数据时,有一个常见误区是只看峰值。峰值确实重要,但峰值的持续时间和形状同样重要。一次持续 3 分钟的尖峰,靠限流和排队就能扛过去;一次持续 3 小时的高位运行,就必须靠真实容量。这两者的应对策略完全不同。
3.3 容量事件档案:最容易被忽略的一类数据
第三类数据是“容量事件档案”,指的是每次扩容、缩容、限流、降级、故障的详细记录。它不属于监控系统,属于团队记忆。
一份有用的容量事件档案,应该包含:时间、触发原因、当时的负载水位、采取的动作、动作后的效果、复盘结论。看起来很简单,但坚持记录的团队很少。
它的价值在于,当你需要预测“流量涨到 X 时会发生什么”时,可以直接检索“历史上 X 附近的负载时发生过什么”。这是把经验固化成数据的最直接方式,也是解决前面提到的幸存者偏差的关键手段。
| 字段 | 示例 | 为什么需要 |
|---|---|---|
| 事件时间 | 某次大促预热开始后 40 分钟 | 对齐流量形态 |
| 负载水位 | 峰值 78%,持续 25 分钟 | 判断拐点位置 |
| 触发原因 | 缓存命中率下降 3 个百分点 | 定位真实瓶颈 |
| 采取动作 | 扩容 6 个实例,调整连接池 | 记录有效手段 |
| 后验效果 | p99 从 320ms 回落到 110ms | 验证因果 |
| 复盘结论 | 缓存容量应作为一级扩容指标 | 形成规则 |
这张表的字段设计对应一个目标:让未来的你能用几条查询,回答“这个水位下我们历史上是什么表现”。
4. 从数据到预测:一个可落地的四步流程
有了数据,还需要一个把数据转成决策的流程。否则数据只是图表,不是预测。下面这套四步流程,是我认为在中小团队也能跑起来的版本。
4.1 第一步:建立基准容量模型
基准容量模型回答的是:在当前版本、当前配置、当前数据规模下,系统在目标延迟内的稳定吞吐是多少。
它的输入是压测数据和近期生产峰值,输出是一张按链路分段的容量表。表里每一段都要写明约束条件,比如“订单服务:单机 1800 QPS,约束是下游库存服务的 p99 不超过 80ms”。
这一步的关键是“分段”,不要试图给出一个全局数字。全局数字看起来很漂亮,但一旦出问题,你无法定位。分段之后,容量预测就变成了逐段对比,哪一段先到红线,扩容就先扩哪一段。
建模时还要留一个“敏感度”字段,记录这一段容量对哪些因素最敏感。比如某段容量对缓存命中率极度敏感,命中率每下降 1 个百分点,容量下降约 3%。这个字段在后面的预测阶段非常有用,因为它告诉你在什么情况下需要重新评估。
4.2 第二步:把业务增长翻译成资源增长
业务侧的预测通常是“用户量增长 50%”“订单量增长 80%”这类表述,它和 QPS 之间需要一次翻译。
翻译的关键是找到业务量和资源消耗之间的比例关系,并且验证这个比例是否稳定。比如日订单量增长 80%,如果客单价结构、下单时段分布、请求合并策略都没变,那么峰值 QPS 大致也增长 80%;但如果新增了批量下单或秒杀场景,峰值系数就会变。
这一步最容易犯的错误是直接按平均值换算。平均值增长 50%,峰值可能增长 120%,因为峰值形态会随业务结构变化。做换算时,建议同时算三个数:日均值、日峰值、以及峰值与均值的比值。比值的漂移往往比绝对值更能说明问题。
json复制{
"scenario": "大促容量评估",
"baseline": {
"avg_qps": 24000,
"peak_qps": 68000,
"peak_avg_ratio": 2.83,
"measured_at": "最近一次全链路压测"
},
"forecast": {
"business_growth": 0.6,
"peak_ratio_drift": 0.15,
"expected_peak_qps": 68000,
"assumption": "峰值系数上浮 15%,因新增整点开抢场景"
},
"conclusion": "峰值预计约 9.4 万 QPS,需在预热前完成对应扩容"
}
这份结构的作用是把假设写在明面上。评审时争论的焦点会从“你觉得行不行”变成“这个 15% 的峰值漂移合不合理”,讨论质量完全不同。
4.3 第三步:留出安全水位与失效预算
算出目标容量之后,不能直接按目标值扩容。你需要留出安全水位,用来吸收三类不确定性:预测误差、突发流量、以及部分实例不可用。
安全水位的设定没有统一标准,但可以参考这个思路:目标峰值占安全容量的比例,控制在 70% 到 75% 之间。也就是说,如果预测峰值是 9.4 万,那么安全容量应该准备到 12.5 万以上。多出来的部分,一半留给预测误差,一半留给实例故障和抖动。
失效预算这个概念来自稳定性工程,放到容量场景里同样适用:你必须提前接受“总会有一些容量损失”,然后为它做好准备,而不是假设一切正常。常见的容量损失来源包括:单可用区故障、部分实例因 GC 或重启不可用、下游限流导致的重试放大。
对于核心链路,建议明确写出降级预案,并且验证过。因为容量不足时的第一反应往往是扩容,但扩容需要时间,而流量不会等你。能在 30 秒内生效的限流规则,有时候比 10 分钟后到位的机器更有价值。
4.4 第四步:上线后回填,让模型自我校准
前三步做完,你得到的是一个预测。预测的价值只有在回填之后才能体现。
回填的意思是:大促或高峰过去之后,把实际峰值、实际瓶颈、实际延迟和预测值做对比,记录偏差方向和幅度,然后修正模型里的系数。偏差不可怕,可怕的是每次都偏同一个方向却没人发现。
回填建议至少覆盖三个维度:峰值预测偏差、瓶颈定位是否准确、扩容动作是否及时。第一个维度校准数字,第二个维度校准链路认知,第三个维度校准流程效率。
如果连续几次回填都发现峰值预测偏低,那说明你的峰值系数需要上调;如果发现瓶颈总是出现在没预料到的环节,那说明链路拆解还不够细。这种持续校准,才是“从经验到数据”真正落地的地方——不是一次性建一个模型,而是让模型随着每次实战变得更准。
5. 那些让容量预测失效的细节,往往不在数学模型里
模型算得再漂亮,也可能被几个工程细节推翻。这些细节不在公式里,但在线上。
5.1 长尾请求、热点与突发流量
平均延迟和平均 QPS 是最容易误导人的两个指标。一个系统平均 QPS 只有 3 万,但某个商品在整点开抢时,单 key 请求可能瞬间占到总量的 40%。这时候瓶颈不在总容量,而在单个缓存分片或单台机器的处理能力。
热点问题的特点是,它不消耗“总量”,它消耗“局部”。你的集群整体利用率可能只有 45%,但某个分片的 CPU 已经 100%。这种情况下,按总量扩容是浪费,按分片拆分或加本地缓存才有效。
长尾请求同理。1% 的请求耗时 2 秒,会让线程池长期有 1% 的线程被占用。当并发上来之后,这 1% 会迅速放大成线程池耗尽。所以容量评估时,除了看 p99,还要看线程池的活跃数和排队长度。
突发流量则考验的是系统的“启动能力”:连接池建立、缓存预热、JIT 编译、限流器初始化,这些都需要时间。一个冷启动的系统,前 30 秒的容量可能只有稳态的一半。如果突发流量恰好落在这个窗口,结果会很难看。
5.2 依赖容量:被忽略的下游放大
上游扩容 3 倍,下游压力也涨 3 倍,这是常识。但实际发生的是更复杂的情况:上游扩容后,单请求的重试次数可能上升,缓存击穿可能导致回源放大,异步补偿任务可能在高峰后集中执行。
一个典型场景是:业务服务扩容后处理能力上来了,但数据库连接池没扩,结果请求全堵在获取连接上,延迟反而更高。从监控上看,业务服务的 CPU 不高,像是“没吃饱”,实际是被下游卡住了。
所以容量评估必须包含下游的容量核对,而且要核对的是“放大后的压力”,不是原始 QPS。核对项至少包括:数据库连接数与慢查询、缓存集群带宽与分片负载、消息队列分区数与消费能力、以及第三方接口的配额限制。
5.3 组织层面的容量共识
技术问题之外,容量预测还有一个组织维度:谁来为这个数字负责。
如果容量预测只是某个团队内部的一次计算,那么它的结论很容易在跨团队协作中被稀释。上游说自己能扛 10 万,下游说自己只能接 6 万,中间没有人对齐,最后出问题时互相指责。
有效的做法是把容量结论变成一份有明确责任人的共识文档:每一段链路的容量上限、责任人、扩容触发条件、扩容所需时间,都写清楚。这样在真正需要扩容时,决策链条是通的,而不是临时开会。
容量评审会上最该问的问题不是“够不够”,而是“如果不够,我们多久能加上,谁来做决定”。前一个问题争不出结果,后一个问题能推动事情。
6. 从一次预测到长期机制:把容量变成可运营的资产
一次成功的容量预测,价值有限。真正有价值的是把这件事变成机制,让每次预测都比上次更准一点。
6.1 建立一份持续维护的容量台账
容量台账不是一份静态文档,而是一份随系统演进的活档案。它至少应该包含:链路分段容量表、历史压测记录、容量事件档案、扩容触发阈值、以及每次预测的偏差记录。
维护成本要控制住,不要一开始就追求大而全。可以从核心链路的三到五个关键服务开始,把这几段的容量、瓶颈、扩容方式写清楚,然后按月更新。跑顺之后,再逐步扩展到全链路。
台账最大的价值在于“可比性”。当你有了半年的连续记录,就能看出容量随版本、随数据量、随流量结构变化的规律。这种规律是任何单次压测都给不了的。
6.2 从新手到进阶的三层落地路径
不同阶段的团队,落地方式应该不同,一上来就做全自动容量预测模型,大概率会烂尾。
第一层是“记录层”:把每次压测、每次扩容、每次故障的容量相关信息记录下来,形成可检索的档案。这一层不需要复杂工具,一张结构化表格加一个固定的记录习惯就能跑起来。它的目标是消除“凭记忆评估”的情况。
第二层是“模型层”:基于积累的数据,建立分段容量模型和峰值系数模型,让预测有据可依。这一层开始需要一些数据加工能力,比如按接口聚合、按小时聚合、计算峰值系数。工具上不一定要自研,用现有的监控和报表能力通常够用。
第三层是“自动化层”:把容量模型接入监控和调度系统,实现阈值触发、自动扩容、以及预测偏差的自动回填。这一层投入大,适合业务规模稳定、流量模式相对清晰的系统。对于流量模式频繁变化的业务,自动化反而可能带来误判,需要人工把关。
| 层级 | 核心动作 | 适用阶段 | 常见失败原因 |
|---|---|---|---|
| 记录层 | 结构化记录压测与容量事件 | 起步阶段 | 记录后不复盘,档案变成死数据 |
| 模型层 | 分段容量模型与峰值系数 | 有一定数据积累 | 系数长期不校准,模型逐渐失真 |
| 自动化层 | 阈值触发与自动回填 | 流量模式稳定 | 突发形态变化时误判扩容 |
6.3 边界:数据也有不可靠的时候
最后要说清楚边界。数据驱动的容量预测不是万能的,它在几种情况下会明显失效。
第一种是业务形态发生结构性质变。比如从浏览型转为交易型,从单件下单转为批量下单,历史数据里的峰值形态完全无法参照。这时候只能靠重新压测和小流量灰度验证。
第二种是新版本引入了新的资源消耗模式。比如一次重构引入了额外的序列化开销,或者新增了一个同步调用。历史容量模型里没有这部分,预测必然偏乐观。所以每次大版本上线后,都值得重新跑一次基准压测。
第三种是依赖侧发生未告知的变化。下游服务扩容了、限流阈值调了、缓存策略改了,这些变化如果不主动同步,上游的容量模型就会失真。这也是为什么容量共识要写清楚责任人。
第四种是极端场景,比如突发性的流量洪峰、依赖服务大面积不可用。这类场景不适合用常规容量模型预测,而应该用预案来应对,靠限流、降级、排队来保证系统不整体崩溃。
所以更准确的说法是:数据让容量预测变得可校准、可追溯、可讨论,但它不消除不确定性。它能做的是把不确定性显性化,让你知道自己在赌什么。
回到开头那个评审会。三个数字 8 万、15 万、30 万,如果只停在数字层面,讨论永远没有结论。但如果把问题换成“在什么假设下是 8 万,什么条件下会变成 15 万,哪一段链路决定了这个上限”,讨论就变成了可推进的工作。容量预测真正的产出从来不是那个数字,而是围绕数字建立起的一套共同认知:瓶颈在哪、余量多少、什么时候该扩、扩不动的时候怎么办。
这套认知建立起来之后,下一次评审会上,你报出的数字可能还是 10 万,但这次没人会追问“你确定吗”,因为假设、边界、排查路径和回退方案都写在纸上了。这才是从经验走向数据真正带来的东西——不是更准的数字,而是更可信的决策过程。
