1 为什么方案里要单独谈「容量估算」
零售系统准备大促时,业务方说“预计有 100 万人参加”,运维方说“已经加了机器”,这 2 句话仍然不能证明订单服务接得住流量。用户可能分散访问,也可能在开售后的 10 秒内集中提交;相同的请求数量,落到不同时间窗口,对资源的要求完全不同。容量估算要把业务规模翻译成请求速率、并发工作量、数据增长和故障时的服务能力,并说明这些数字依赖哪些假设。
本课始终使用同 1 条零售订单链路:客户端提交订单,网关完成认证与限流,订单 API 校验商品和价格,调用库存服务预占库存,把订单及待发送事件写入数据库,再由后台任务发送事件。付款与履约在后续流程完成。接口成功的含义是订单已可靠落库且库存预占符合约定,并不意味着商品已经发出。先明确成功边界,后面的吞吐量和延迟才有共同口径。
假设活动高峰的有效下单流量为 2400 请求/秒,业务要求在约定负载下,成功请求的 P95 延迟不超过 300 毫秒。P95 表示观测窗口内有 95% 的请求延迟不高于该值。系统还需要承受 1 个可用区退出,并在扩容生效前吸收短时尖峰。容量讨论由此变成可检验的问题:正常需要多少实例,失去部分实例后还剩多少能力,哪些请求可以等待,哪些必须立即拒绝。
估算不是对未来作精确预言,而是给设计提供带边界的依据。方案应保留输入来源、测量条件、计算方法和待验证事项。业务预测偏差、单机能力变化和依赖限额都可能让结果失效。与其写“可支撑高并发”,不如写清“在指定请求组合、数据库状态和故障条件下,经过测试能持续处理多少请求”。这样业务调整活动时间或增加优惠计算时,团队能够定位需要重算的部分。

2 核心概念与边界
2.1 请求速率、吞吐量与并发
请求到达率表示每秒有多少请求进入系统,常用 RPS;完成吞吐量表示每秒完成多少请求。系统稳定时,两者长期接近;发生拥塞时,到达率可能高于完成速率,差额积累在队列中或被拒绝。报告“处理了 3000 RPS”时,要同时说明是发送量、接收量还是成功完成量,以及错误率和持续时间。快速返回错误也能制造很高的完成吞吐量,却没有创造有效订单。
并发表示同一时刻还在系统中的工作数量。在观测边界一致、系统稳定且平均值可代表该窗口时,可以用 Little 定律估算:平均在途数量 L = 平均到达率 λ × 平均停留时间 W。若每秒完成 2400 个请求,平均停留 0.12 秒,则平均在途请求约为 288 个。这里必须使用平均停留时间,不能把 P95 的 300 毫秒直接代入并声称得到精确并发。排队时间也属于所选边界内的停留时间。
连接数、线程数与在途请求数也不是同义词。连接可能保持空闲;异步服务可用较少线程等待许多远程调用;某个请求还可能同时占用多个下游连接。估算连接池需要知道每次请求访问数据库的次数、每次占用连接的时间,以及同一时刻有哪些查询重叠。仅凭 API 并发数设置所有连接池,往往会把压力集中推向数据库。
2.2 业务口径与负载组合
容量输入至少要区分浏览、提交订单、查单和后台任务。本课的 2400 RPS 指有效下单意图到达订单入口的速率,尚未包含失败后的客户端重试。若订单入口之前还有机器人流量或无效签名,网关也需要单独计算处理能力。不能用“每日访问量除以 86400”代替开售峰值,也不能把日活用户直接当作同时下单人数。
请求组合决定每个请求的成本。单商品订单与包含 20 个商品的购物车,可能产生不同的库存调用数、数据行数和序列化开销。使用优惠券、跨仓发货、首次用户校验,也会改变依赖路径。测试应给出这些请求的比例和数据分布,还要覆盖热点商品。随机访问大量商品得到的容量,不能直接承诺用于所有用户争抢同 1 个库存记录的场景。
2.3 吞吐容量与空间容量
吞吐容量回答单位时间内能完成多少工作,空间容量回答需要保存多少数据。假设每天新增 200 万个订单,每单主记录、明细及必要索引合计约 4 KB,按十进制估算,每天逻辑新增约 8 GB,保留 180 天约为 1.44 TB。这个数尚未包括复制副本、备份、事务日志、临时空间和运维余量,也没有证明磁盘的读写吞吐足够。
空间估算还要说明保留和清理策略。若过期数据只加标记、不释放空间,按业务保留天数推算物理占用会过于乐观。日志可能随错误率和重试量增长,促销期间的增长速度与订单表不同。CPU、内存、连接、存储空间、存储读写和网络分别有容量约束;最终承载能力由最先触及限制的环节决定。
3 关键机制
3.1 把峰值拆成可计算的需求
从业务预测开始,先选时间窗口,再估计窗口内参与人数、每人提交次数和集中程度。假设 1 分钟内有 96000 个有效下单意图,分钟平均为 1600 RPS;根据类似活动的秒级分布,最忙连续窗口约达到分钟平均的 1.5 倍,于是得到 2400 RPS。这个倍数是有待验证的预测输入,应注明历史活动或演练来源。更细的 1 秒峰值仍可能超过它。
重试必须独立建模。若每个原始请求平均额外发出 0.1 次重试,入口压力变成 2640 RPS。这个比例只适用于给定故障状态;大量请求超时后,重试比例可能急剧上升。客户端重试、网关重试和服务内部重试若同时存在,放大效果会叠加。容量表应写明在哪层允许重试、最多几次、是否退避,以及总超时预算,不能靠固定加成掩盖无限重试。
3.2 测出满足目标的单实例能力
在接近生产的配置下逐步提高负载,找到延迟、错误率或资源约束开始恶化的位置。假设某版本单实例在指定负载组合下,持续完成 500 RPS 时,P95 为 220 毫秒,错误率低于约定阈值,资源仍在可控范围;继续提高到 600 RPS 时,排队和尾延迟明显上升。方案可以把 500 RPS 作为本轮测得的单实例参考能力,但不能把 600 RPS 的短时峰值当作长期承诺。
测试结果要绑定实例规格、软件版本、连接池、依赖配额、数据量与压测方式。若负载发生器因等待响应而减少发送,请求变慢后实际到达率也会下降,从而掩盖拥塞。使用能按计划产生到达流量的测试方式,并同时记录目标发送率与实际发送率,才能判断服务是否真的承受了规定压力。测试端的 CPU、网络和连接上限也要排除。
3.3 从正常实例数推到故障实例数
本例为了预留预测误差和扩容窗口,把正常规划负载控制在实测单实例参考能力的 60%,因此每实例规划承载为 500 × 0.6 = 300 RPS。这里的 60% 是容量折扣系数,并不是“CPU 必须为 60%”。正常所需实例数为向上取整的 2640 ÷ 300,即 9 个。CPU 指标需要另行观测,不能把请求吞吐比例与 CPU 利用率混为同 1 个指标。
若实例均匀放在 3 个可用区,要求任意 1 个可用区退出后仍按每实例 300 RPS 规划,则剩余实例数至少为 9 个。均匀部署时最少需要 15 个实例,每区 5 个,失去 1 区后剩余 10 个,规划能力为 3000 RPS。12 个实例在退出 1 区后只剩 8 个,规划能力 2400 RPS,不能覆盖包含重试的 2640 RPS。整数取整和放置规则会直接影响结果。
3.4 队列、扩容与背压
队列能缓冲短时差额,不能创造处理能力。若到达率为 3000 RPS,处理率为 2600 RPS,持续 30 秒会积累约 12000 个请求。忽略新请求时,清空这些请求也约需 4.6 秒;如果流量仍高于处理率,队列不会自行消失。同步下单接口的延迟目标只有 300 毫秒,把大量请求放进长队列通常意味着超时,需要更早限流或明确改为异步受理。
扩容也有检测、调度、启动、预热和接收流量的过程。假设总耗时 90 秒,这段时间需要已有容量、入口限流或可控缓冲共同承担。扩出来的实例还会建立连接、加载缓存,可能短时放大下游压力。背压是让上游知道下游已经吃紧,并减少继续提交的机制,例如限制并发、拒绝超出预算的请求、向客户端提供适当的重试提示。它必须与业务的失败语义配套设计。

4 落到订单链路的关键点
4.1 按调用次数展开依赖需求
订单入口 2640 RPS 不代表所有下游也都是 2640 RPS。假设每次提交平均进行 1 次价格查询、2 次库存预占操作、1 次数据库事务,则对应需求分别约为 2640、5280 和 2640 次/秒。库存操作的均值还要和商品数分布一起看;长购物车可能占用更多连接与事务时间。缓存命中率决定查询落到源站的比例,而预占库存的写操作通常不能仅靠读取缓存完成。
数据库事务每秒 2640 次,也不等于每秒只写 2640 行。每个事务可能插入订单主表、商品明细、状态记录和事件表,同时更新索引并产生事务日志。估算时需要记录每单平均明细数和每事务读写行数,再通过实测观察锁等待、日志写入与磁盘延迟。热点库存记录即使数据量很小,也可能由于串行争用成为瓶颈。
4.2 给同步路径分配预算
300 毫秒目标需要覆盖网关、应用处理、库存调用、数据库提交以及网络等环节。可以先为各环节设定工作预算,用调用链数据检查是否合理,但不能简单把各环节 P95 相加,当作端到端 P95 的精确预测。不同环节的慢请求可能相关,也可能落在不同请求上。最终是否达标,应以同 1 请求的端到端分布验证。
调用超时要留出后续提交或清理的时间。库存请求若已经耗尽接口总预算,继续长时间等待数据库,会让客户端重试与服务端原请求重叠。使用稳定的业务请求标识实现幂等,使重复提交不会创建重复订单;但幂等检查本身也会消耗存储与连接,仍需纳入容量。请求超时也不代表订单一定没有创建,查单或结果查询路径因此同样需要预算。
4.3 把异步工作算到同 1 张账上
订单落库后发事件,可以缩短同步路径,但后台任务仍需处理订单量。使用事务内写入待发送事件的方式,可以把订单与事件意图一起提交;发送器再读取并投递,必须考虑重复投递、发送失败和重放。假设每单产生 3 个事件,高峰约需处理 7920 个事件/秒。事件数与消息大小共同决定网络和存储需求。
消费者容量不仅要覆盖实时新增,还要覆盖故障后的追赶。若暂停期间积压 120 万个事件,恢复后新增速率为 6000 个/秒,处理速率为 10000 个/秒,净消减速率为 4000 个/秒,约需 300 秒追平。用总处理速率计算清空时间会低估恢复时长。恢复过程还要限制对数据库和外部服务的冲击,否则追赶会再次拖慢订单入口。
4.4 明确余量对应的风险
容量折扣、可用区容灾、发布期间不可用实例和业务增长分别解决不同问题。若同 1 份空闲资源同时被用于承诺故障承载、版本发布与翻倍流量,就可能发生重复计算。方案应列出哪些事件要求同时发生,哪些允许错峰,以及异常时允许保住哪些能力。例如大促期间可以暂停批量导出,为订单写入释放资源,但需要写明执行条件和恢复方式。
5 动手:控制台/命令直觉
5.1 建立可复算的输入表
先在本地表格或方案文档中建立输入表,给每个数标上“业务预测”“实测”或“暂定”。预测包含 2400 RPS 原始峰值和 0.1 的平均额外重试次数;实测包含单实例 500 RPS 的参考能力;暂定项包含 0.6 的规划折扣和 90 秒扩容耗时。再写出计算列:入口需求 2640 RPS、每实例规划 300 RPS、正常 9 个实例、按 3 区容灾部署 15 个实例。
改变参数时应观察整条链路,而不只看总实例数。折扣提高会减少机器,但缩短了应对误差的空间;折扣降低会增加实例及其连接池总量,如果数据库连接上限没有调整,可能反而造成连接竞争。下面的滑块用于观察这个取舍。它给出在固定场景中的算术结果,最终承诺仍要经过故障与负载测试。
5.2 在测试环境查看资源与请求
若应用运行在 Kubernetes,可以先用下面的只读命令查看副本、资源占用与伸缩状态。命名空间和工作负载名称需要替换成测试环境中的实际名称;命令输出是即时切片,不能替代监控中的时间序列。资源请求与限制、实际使用量和服务承载能力也要分别解释。
kubectl -n retail-test get deployment order-api
kubectl -n retail-test get pods -l app=order-api -o wide
kubectl -n retail-test top pods -l app=order-api
kubectl -n retail-test get hpa order-api
kubectl -n retail-test describe deployment order-api
重点观察期望副本与就绪副本是否一致,实例是否分散到不同可用区,是否存在启动缓慢、重启或资源限制。CPU 平均值低不代表系统没有瓶颈:数据库等待会使线程阻塞,CPU 反而不高;CPU 限流可能发生在短时间片内,分钟均值无法解释。把实例级指标与入口延迟、连接等待放到相同时间轴上,才能判断资源的实际约束。
5.3 让压测回答具体问题
测试应使用隔离环境、测试商品与可清理的订单数据,避免产生真实扣款或履约。先在低负载下校验返回语义、幂等和数据一致性,再按计划逐步提升到 1200、1800、2400、2640 RPS。每档等待指标趋于稳定,并保留进入该档前后的时间窗口;具体持续时间取决于缓存、垃圾回收和后台任务周期,不能只截取最好的 10 秒。
随后加入峰值突增、持续运行和退出 1 个可用区的演练,记录实际发送率、成功订单率、P95/P99、重试量及依赖状态。压测生成器应有独立的容量监控和明确停止条件,例如错误率持续超阈值或数据库剩余连接过低时停止加压。把停止视为测试证据:它说明当前边界在哪里,而不是把失败样本从结果中删掉。
6 观测字段与告警
容量观测需要把需求、完成量、等待和饱和程度连起来。入口请求日志建议包含时间、路由、请求标识、业务幂等键的安全摘要、状态码、业务结果、总耗时、重试序号和限流原因。调用链中记录依赖名称、耗时、超时原因与实例所在区。不得记录付款凭据或完整个人资料;字段的保留时间和访问权限也要与用途一致。
指标侧至少区分入口到达 RPS、成功完成 RPS、拒绝 RPS、超时 RPS,以及在途数量、队列长度、最老任务年龄、连接池等待、数据库提交耗时和锁等待。业务上还要观察成功创建订单数、重复提交命中数、库存预占失败数和事件未发送数量。技术上的 HTTP 成功不能替代业务成功;业务缺货也应与系统错误分开,避免活动售罄时误判系统故障。

延迟告警应按请求类型分组,并绑定流量窗口。低流量下少量慢请求就可能使分位数波动,需要结合样本数解释。系统级 P95 应由可合并的直方图等方式计算,不应直接平均各实例的 P95。请求标识适合日志与追踪,不宜作为指标标签,否则标签组合会无限增长,监控系统本身也会受到容量压力。
告警要指向行动。若成功率下降且连接池等待上升,可以触发值班排查与入口保护;若任务最老年龄接近业务允许时限,应限制非核心后台工作并评估追赶能力。CPU 高但延迟稳定时未必需要立即限流,而连接等待快速增长、入口吞吐高于完成吞吐,通常说明正在形成积压。阈值应通过测试与历史基线确定,写明持续窗口、恢复条件及负责人员。
容量余量也需要持续观测。若故障后剩余 10 个实例,当前需求为 2640 RPS,规划能力为 3000 RPS,则按该规划口径剩余约 360 RPS。这个差额不是可靠的瞬时性能保证,还受流量分配、热点及下游状态影响。每次版本改变、商品组合改变或依赖配额调整后,都应评估参考能力是否仍然有效。
7 常见坑
把日均值当峰值,会掩盖集中开售;把最短窗口的最高值无限外推,又可能造成过度采购。应同时保留秒级峰值、持续峰值和全天总量,分别服务于限流、稳定承载和数据空间规划。对缺少历史数据的新活动,可给出低、中、高情景,并明确触发预扩容和限制流量的条件,而不是假装某个单值已被证明。
把“CPU 还有空”理解成“容量还有空”,容易错过数据库锁、连接池和下游配额等约束。增加订单实例后,所有实例连接池上限之和可能超过数据库允许连接数。若 15 个实例各配置 100 个连接,总上限就是 1500;还要为后台任务、管理连接和其他服务保留份额。连接数应按共享依赖的总预算分配,并验证连接持有时间。
用缓存预热后的成绩承诺冷启动能力,或用小数据集承诺生产规模,也会高估容量。索引大小、访问倾斜、历史数据和后台维护都会改变查询成本。容量报告应说明缓存状态和数据规模,并单独观察冷启动、缓存失效与热点场景。缓存击穿时的源站压力可能远超日常平均,入口需要避免把全部未命中请求同时推给数据库。
依赖扩容自动化而没有预留生效时间,会让队列在新实例到来前先把延迟拖垮。盲目增加重试则会让原本短暂的慢响应变成持续拥塞。重试应限定在有希望恢复且具备幂等语义的操作上,采用退避和适当随机化,并共享总时间预算。明确的超载拒绝比超时后未知结果更容易让客户端做出正确处理。
还有 1 个容易忽略的错误:用请求限流冒充订单一致性保障。限流可以减轻压力,却不能保证库存不会重复扣减,也不能消除超时后的重复提交。容量设计与幂等、事务、库存约束需要一起验证。相反,为追求“绝不拒绝”而无限排队,会耗尽内存并让所有用户等待更久,应在资源与业务预算内给出明确结果。
8 案例:大促或故障
某次大促采用本课的规划:3 个可用区各部署 5 个实例,总计 15 个。预计有效下单 2400 RPS,加上重试后入口 2640 RPS。活动开始前完成预热,确认库存和数据库能够承受对应调用量,并暂停可延后的批量报表。入口限流策略考虑瞬时波动,数据库连接预算按所有实例与后台任务的总和配置。
开售后,某个可用区网络异常,5 个实例逐渐退出服务。流量转移期间仍有少量请求打到异常实例,客户端出现重试;剩余 10 个实例平均约承担 264 RPS,低于规划的 300 RPS,但短窗口流量分配并不均匀。值班人员需要观察最忙实例和跨区依赖延迟,不能只看集群平均值。健康检查、摘流与已有连接的处理决定切换过程是否平滑。
若重试突然升至每个原始请求平均额外 0.4 次,入口就会达到 3360 RPS,超过故障状态的 3000 RPS 规划能力。团队先按预案控制重试和入口并发,保护已受理订单,给超载请求明确结果;同时确认数据库状态,再逐步增加承载。不能因为应用 CPU 尚未到顶,就解除对下游的保护。
故障恢复时也不能立即把所有积压工作开到最大。重新加入的实例需要预热,发送器与消费者需要按净消减速率安排追赶。假设积压 120 万事件,处理能力和新增量符合前文条件,预计约 300 秒追平;若最老事件年龄没有持续下降,就说明估计中的处理能力没有兑现,或者还有未计入的新增工作。
演练结束后,团队不应只记录“活动成功”。应保留切换耗时、最大重试放大、最差成功率、延迟恢复时间与积压清空时间,并修正预测。若退出可用区后数据库连接建立耗时明显增加,下一轮要验证连接预留与重连退避;若限流误伤已受理订单的查询,则要为查单分配独立预算,使用户能够确认结果。
9 排查案例:证据链
9.1 先描述现象与时间边界
另 1 次演练中,入口目标保持 2640 RPS,订单 P95 从 220 毫秒上升至 900 毫秒,超时增加,而应用 CPU 仅约 45%。先固定故障窗口与时钟口径,核对生成器实际发送率、网关接收率和成功订单率。若实际发送率已经下降,不能把“服务仍完成 2640 RPS”当作既定事实;若只有某类购物车变慢,则应按请求组合继续拆分。
9.2 用等待位置缩小范围
调用链显示应用计算耗时变化不大,数据库连接获取耗时从数毫秒升至 180 毫秒,事务提交又从 30 毫秒升至 260 毫秒。连接池等待并不自动证明池子太小:连接被长事务占住时,扩大池子可能加剧数据库争用。此时需要查看活跃连接、事务持续时间、锁等待与慢查询,而不是直接把连接上限翻倍。

9.3 找到变化并验证假设
数据库监控显示热点库存记录的锁等待升高,同期版本把库存审计写入同步事务,延长了锁持有时间。日志与追踪中的请求标识把慢订单关联到该路径,版本时间线又与异常起点吻合。这些证据支持“事务变长导致连接占用增加,再引起排队”的假设,但时间吻合本身还不能证明因果,需要在同样数据和负载下验证。
团队在测试环境回退该项变更,保持商品分布、请求组合、实例数与到达率一致,观察锁等待、连接持有时间和端到端延迟是否一起回落。若只回落 CPU 而订单延迟未恢复,就不能宣告原因已找到。必要时补充对热点与普通商品的对照,排除生成器瓶颈和缓存状态变化。每次验证应尽量只改变目标因素。
9.4 把结论写回容量假设
验证后,旧路径恢复到参考水平,新路径的单实例能力则需要重新测定。故障记录应写出症状、证据、排除项、干预动作和结果,并说明哪些结论仍然有限。例如本次验证只覆盖既定购物车规模,尚未覆盖更长明细。容量表更新参考版本与测量日期,再评估 15 个实例是否仍满足目标。实例数量是计算结果,业务边界和经验证的服务能力才是持续维护的依据。
放进方案里
容量章节应先写业务边界:订单入口在活动高峰预计承接 2400 RPS 有效下单意图,采用已约定的商品数、优惠与热点分布;成功指订单可靠落库且完成库存预占,端到端 P95 目标为 300 毫秒,并单列系统错误率与业务缺货率。预测注明来源、适用时间窗口和负责人,不能只留下最终实例数。
随后给出可复算依据:按平均额外重试 0.1 次计算,入口需求为 2640 RPS。指定版本与测试配置下,单实例参考能力为 500 RPS,规划折扣为 0.6,每实例规划承载 300 RPS。正常需求为 9 个实例;采用 3 个可用区均匀部署、允许失去 1 区的约束,部署 15 个实例,故障后剩余 10 个,规划能力为 3000 RPS。重试上升或依赖降速需要重新评估。
依赖预算应列出价格查询、库存操作、数据库事务、连接总量及事件速率,并标记热点约束。数据空间按每天订单量、每单含索引大小及保留周期估算,再补充副本、备份、日志与清理余量。异步部分写明允许的最老事件年龄、故障积压和净追赶能力,避免用后台处理掩盖未完成的业务义务。
最后写验证与运行约定:报告附上请求组合、数据规模、实际发送率、成功吞吐、延迟分布及可用区退出结果。扩容预留 90 秒的暂定窗口,由演练确认;入口限流、重试退避和后台暂停均明确触发条件与责任人。版本、业务组合或依赖配额变化后复测,尚未验证的假设列为上线前待办,批准条件以证据是否齐备为准。
容量结论写入方案时,还要附一份「拒绝扩容」的条件:若依赖配额、连接池或共享缓存热点未解,仅增加计算实例不会把端到端成功率带回目标;此时应先改依赖预算或分流策略,再重算实例数。把这条否决条件写进审批清单,可避免把容量章节做成只加机器的惯性动作。
10 小结:检查清单
- 是否把业务规模转换为明确时间窗口内的请求速率,并区分原始请求与重试?
- 是否用平均停留时间估算平均并发,同时单独验证 P95/P99 延迟?
- 单实例能力是否绑定版本、请求组合、数据规模及成功率目标?
- 是否计算故障后剩余能力、实例放置与扩容生效前的承载?
- 是否展开库存、数据库、连接、消息和存储的独立预算?
- 是否规定队列长度、最老任务年龄、超载语义及追赶时间?
- 是否有从业务结果到资源等待的证据链,并把验证结果写回容量表?
下一课转向 P5 架构权衡:把本章验收过的结论带进后续主题,写成可检查的方案约束。