容量评估:怎样把 QPS、并发数与响应时间换成资源预算
从业务流量模型出发,计算峰值 QPS、在途并发、CPU、内存、连接、数据库、缓存、消息队列、网络与冗余容量,并用压测和线上指标校准整份容量计划。
“单机能扛多少 QPS”常被当成容量问题的起点。这个数字脱离请求类型、响应时间和测试条件后几乎没有意义:同一个接口可能在缓存命中时消耗 2 ms CPU,在缓存未命中时占用一个数据库连接 200 ms;服务可以在 p99 达到 5 s 时勉强完成 2 万 QPS,也可以在 p99 小于 200 ms 的约束下稳定完成 8,000 QPS。两者不能算作相同容量。
容量评估要回答一个问题:在指定流量形状、数据规模、依赖状态和服务目标下,系统能够持续处理多少工作,并为峰值、发布、故障与增长预留多少资源?最终产物是一组相互约束的预算,包括实例数、CPU、内存、在途并发、连接池、数据库事务与 I/O、缓存内存和带宽、消息积压、网络吞吐,以及失去部分容量后仍能服务的余量。单独一个 QPS 无法表达这些边界。
本文继续使用上一篇系统设计方法中的活动报名系统。假设一场活动有 100 万关注用户,开放时形成尖峰;系统提供活动详情、报名提交和结果查询三个主要接口。上一篇只用少量计算暴露数量级,这一篇会把完整容量模型算到底,并说明哪些数字来自业务预测,哪些必须通过压测和线上观测获得。
一、先定义“容量”究竟指什么
容量至少包含吞吐、延迟、错误率和持续时间四个条件。下面两句话描述的是不同能力:
系统曾在 30 秒内接收 50,000 QPS,p99 为 3.8 s,错误率 7%
系统可持续 30 分钟处理 18,000 QPS,p99 小于 200 ms,错误率低于 0.1%
第一条可能只是压力峰值,第二条才接近可以用于生产规划的服务容量。容量记录必须绑定工作负载、软件版本、资源规格、数据规模和 SLO,否则下次发布后无法判断旧结果是否仍有效。
系统还有多种容量上限:
| 上限 | 含义 | 常见先兆 |
|---|---|---|
| 吞吐上限 | 单位时间完成的工作不再增加 | QPS 持平,队列继续增长 |
| 延迟上限 | 吞吐尚可,但 p99 已超过 SLO | 排队时间、锁等待或 GC 上升 |
| 正确性上限 | 高负载下超卖、丢消息或读到错误状态 | 不变量告警、对账差异增加 |
| 稳态上限 | 短测能通过,持续运行会耗尽资源 | 堆积、磁盘、连接泄漏缓慢增长 |
| 故障容量 | 损失实例、可用区或依赖后剩余能力 | 切流后健康节点立即过载 |
生产容量通常取这些边界中最小的一项,并额外保留安全余量。若数据库在 12,000 TPS 时锁等待失控,即使应用实例能跑到 30,000 QPS,整条请求路径也只能按数据库边界规划。
容量、负载与利用率不是同一个概念
负载(load)是系统收到的工作,例如每秒请求数、每秒字节数或待处理任务数。容量(capacity)是在目标约束下能处理的工作。利用率(utilization)表示某种资源已被使用的比例。CPU 40% 不代表系统还有 60% 业务容量:请求可能阻塞在数据库锁上,单线程事件循环可能已经饱和,某个分片也可能先达到 100%。
反过来,CPU 90% 也不必然意味着已经过载。纯计算服务在延迟稳定、队列有界时可以运行在较高 CPU;对延迟敏感且流量突发的在线服务则需要更大余量。目标利用率要根据负载波动、扩容速度和过载曲线确定,不能从一个通用百分比抄到所有系统。
二、先建立工作负载模型,再计算资源
日活用户数不能直接换算成 QPS。用户怎样到达、每次会调用几个接口、读写比例如何、请求成本是否一致,都会改变下游容量。工作负载模型至少要包含以下维度:
- 请求类型:活动详情、报名提交、结果查询和运营写入分别统计;
- 到达形状:均匀、周期波动、开场尖峰、批任务或故障重放;
- 请求大小:请求体、响应体、缓存对象和消息字节数;
- 成本分布:缓存命中与未命中、成功与失败、简单活动与复杂资格校验;
- 访问倾斜:请求是否均匀落到活动、用户和分片;
- 放大关系:一次入口请求产生多少 RPC、SQL、缓存访问和消息;
- 重试与重复:客户端刷新、网关重试、消费者重投分别增加多少工作。
图中的倍率不能只写平均值。报名提交平均产生三次 SQL,但成功请求可能产生四次,名额已满的请求只做一次条件判断,结果未知后的查询又形成另一条路径。容量表应保留各类请求占比与单次成本,活动规则改变时才能重新计算。
从业务事件推导入口 QPS
假设 100 万用户关注活动,30% 会在开放后的 10 秒内点击报名,页面刷新和网络重试让每个用户平均提交 1.3 次:
报名请求总数 = 1,000,000 × 30% × 1.3 = 390,000
10 秒平均 QPS = 390,000 ÷ 10 = 39,000
这个 39,000 是十秒窗口平均值,不能代表每一秒。若第一秒承接 35% 请求,首秒会达到 136,500 QPS。监控按一分钟聚合时,这个尖峰可能只显示为 6,500 QPS。容量预测因此需要业务给出时间分布,或者从历史活动提取 1 秒、10 秒、1 分钟等多个窗口的峰值系数。
活动详情的负载也不能用报名 QPS 代替。若每个报名用户在开放前刷新页面四次,详情读取可能达到报名写入的数倍;若详情页面包含五个并行接口,前端一次刷新会进一步放大网关 QPS。入口层应该按 API 统计,后端资源则按实际调用图继续展开。
日常增长与计划事件分开预测
自然增长可以根据最近数周的同周期峰值、用户增长和功能渗透率预测。营销活动、迁移、批量补偿和客户端版本发布属于计划事件,不能埋进一条平滑趋势线。Google SRE 的容量规划说明把 organic demand 与新功能、营销等 inorganic demand 分开考虑,并要求定期通过负载测试校准资源和服务容量的关系。
预测最好给出基准、保守和压力三个场景,并记录依据。例如基准峰值 4 万 QPS、保守 6 万、压力 10 万;上线准备按保守场景配置,压力场景用于验证限流和降级。一个只有“预计 5 万 QPS”的计划,无法表达预测误差和超出预测后的行为。
三、Little’s Law 把吞吐、并发和延迟连在一起
John D. C. Little 在 1961 年发表的排队公式证明给出稳态系统中的关系:
L = λ × W
L:系统内平均工作数量
λ:有效到达率
W:每项工作在系统内的平均停留时间
若报名服务完成 39,000 请求每秒,平均端到端时间为 100 ms,平均在途约为 3,900。平均时间上升到 500 ms,在途会增加到 19,500。即使入口 QPS 没变,连接、请求对象、线程或协程、缓冲区以及下游操作都会显著增加。
Little’s Law 是观察关系,不会告诉系统应该配置多少线程。它成立还依赖统计窗口内系统接近稳态、进入与离开的工作能够对应。若到达率长期大于完成率,队列一直增长,系统没有达到稳态;此时用入口 QPS 乘平均延迟估算并发会漏掉尚未完成的堆积。
给每一段等待分别计算在途量
一次请求的 200 ms 可能由 10 ms 应用 CPU、20 ms 连接池等待、120 ms 数据库执行和 50 ms 网络及序列化组成。端到端并发约等于 QPS × 200 ms,数据库活跃连接只对应实际占用连接的时间,不能直接按 200 ms 计算。
入口在途请求 = 20,000 QPS × 0.200 s = 4,000
数据库占用连接 = 20,000 QPS × 0.120 s = 2,400
CPU 核需求 = 20,000 QPS × 0.010 CPU-s = 200 cores
这里暴露了一个不可行方案:单个数据库不可能仅靠设置 2,400 个连接就获得对应吞吐。大量连接会争抢 CPU、锁和缓冲池,还增加上下文切换。团队需要减少访问次数、缩短事务、分散写入或降低入口速率。连接池大小必须由数据库能够有效并行的工作量反推,不能为了消除应用等待无限调大。
四、排队和利用率决定尾延迟何时失控
资源利用率升高时,空闲间隙减少,请求更容易碰到正在执行的工作。吞吐在一段区间内近似线性增长,随后排队时间快速上升;超过处理能力后,完成吞吐不再增加,队列与超时继续增长。这个从平稳区进入非线性区的位置常被称为容量拐点。
图里的 70% 只是示意,不是通用阈值。请求成本越不稳定、扩容越慢、突发越强,安全运行点通常离拐点越远。压测需要同时画吞吐、p50/p99、错误率、排队时间和资源利用率,才能确认哪个点开始违反目标。
平均延迟会掩盖排队问题。九成请求耗时 20 ms,一成请求耗时 2 s,平均值约 218 ms;对于一分钟内发起数十次调用的页面,碰到长尾的概率很高。容量验收至少要看与 SLO 对齐的高分位,还要按接口、响应大小、缓存命中和分片拆开。
队列只吸收短突发
假设服务可稳定完成 20,000 QPS,入口持续 30,000 QPS,每秒净增 10,000 个请求。一个可容纳 50,000 项的队列只能多争取五秒,而且排在队尾的请求在开始执行前已经等待数秒。用户 deadline 若是 1 s,大部分工作执行时已经没有调用方等待结果。
队列预算应同时指定最大条数和最大等待时间:
queue_capacity = sustainable_qps × allowed_queue_wait
20,000 × 0.050s = 1,000 requests
这个简化公式说明 50 ms 等待预算只能容纳约一千项,并不代表队列一定要配置成一千。还要考虑请求成本差异、并发执行数和突发到达。在线请求通常适合短队列和快速拒绝;必须可靠执行的长任务应进入持久化消息系统,由独立完成时间目标约束。
五、把请求换算成应用计算资源
应用层容量可以从每请求 CPU 时间、内存占用、并发和网络字节开始。每请求 CPU 时间必须通过 profiler、运行时指标或隔离压测获得,墙钟时间不能直接代替。一次请求等待数据库 100 ms、实际使用 CPU 2 ms,它对实例并发影响很大,对 CPU 需求却只有 2 ms。
假设报名请求平均使用 4 ms CPU,在 20,000 QPS 下理论计算需求为:
CPU demand = 20,000 × 0.004 CPU-s = 80 cores
若单 Pod 申请 2 core,目标 CPU 利用率 60%,每个 Pod 可预算 1.2 core,有效承载约 300 QPS:
per_pod_qps = 1.2 core ÷ 0.004 CPU-s = 300 QPS
base_pods = ceil(20,000 ÷ 300) = 67
这个计算没有包含 GC、运行时、sidecar、日志、TLS 和成本更高的少数请求,因此只能作为起始模型。压测若显示每 Pod 在目标 p99 下只能承载 240 QPS,应使用 240 作为版本化实测系数,并检查理论与实测差异来自哪里。
内存预算按常驻与在途拆开
应用内存由运行时基础占用、代码与缓存、每个在途请求、线程栈或协程状态、连接缓冲区和临时对象组成:
memory = baseline
+ local_cache
+ in_flight × bytes_per_request
+ connections × buffer_bytes
+ safety_margin
若每个在途请求平均占 32 KiB,单 Pod 在途 600 个,仅请求状态约 18.75 MiB;若响应包含 1 MiB 文件并被完整缓冲,同样并发会需要约 600 MiB。平均对象大小不足以覆盖大响应长尾,需要分别限制请求体、响应体、批量大小和单用户并发。
JVM 服务还要观察堆外内存、直接缓冲区、线程栈与容器 limit。只按 Java heap 设置 Pod 内存,可能在堆尚未满时被容器 OOMKill。容量测试应持续到垃圾回收周期稳定,短短几十秒的测试经常看不到晋升、缓存增长和内存泄漏。
六、数据库预算由事务成本和冲突共同决定
数据库容量不能只写“主库 2 万 QPS”。一次查询与一次事务的 CPU、I/O、锁持有时间和日志量差异很大。报名写路径可以拆成:幂等查询、报名插入、库存条件更新、Outbox 插入和事务提交。读请求还要区分主键点查、范围扫描、回表与聚合。
建议为每类 SQL 记录以下实测项:
| 指标 | 它约束什么 |
|---|---|
| 执行次数 / 业务请求 | 入口到数据库的放大倍数 |
| p50/p99 执行时间 | 连接占用与尾延迟 |
| rows examined / returned | 索引效率与 CPU 浪费 |
| redo / WAL bytes | 日志盘与复制网络 |
| 锁等待和死锁 | 热点事务的并行上限 |
| buffer hit 与磁盘读取 | 内存和 IOPS 需求 |
| 复制延迟 | 读副本新鲜度与故障恢复 |
存储容量应同时计算业务行、索引、版本、日志和备份。若每条报名记录连同索引平均 800 B,一亿条约 80 GB;再考虑页填充、多个副本、binlog/WAL 保留、备份和在线 DDL 临时空间,实际预算会大得多。增长预测还要换算到扩容提前量,不能等磁盘达到 90% 才开始迁移。
热点让总 TPS 失去代表性
十个活动平均分散 2 万 TPS,与一个活动独占 2 万 TPS 的结果不同。报名库存条件更新若集中在同一行,事务会串行争锁;数据库总 CPU 可能只有 30%,该热点行已经决定完成吞吐。容量测试必须保留真实 key 分布,并分别报告集群总吞吐和最热分片、最热行的延迟。
增加连接无法解决热点锁。连接越多,等待者越多,事务内存和调度开销也越大。优化路径可能是缩短事务、减少同一行更新、库存分桶、按活动进入单写者,或者在入口提前拒绝明显超过剩余名额的流量。选择哪种方式属于热点治理,后续文章会展开;容量表应先把热点上限作为独立资源记录。
七、缓存预算同时受内存、网络与热点击穿约束
缓存容量要从对象数量、对象大小、元数据、复制、碎片和淘汰余量计算。若 200 万个活动对象平均 2 KiB,仅值数据约 3.8 GiB;key、对象结构、分配器碎片、过期字典和复制都会增加占用。生产环境不能按 对象数 × value 大小 把内存刚好填满,否则后台持久化、复制缓冲和流量波动都可能触发风险。
吞吐还可能先被网络限制。Redis 官方基准说明举例说明,4 KiB value 在 10 万次每秒时仅数据方向就约为 3.2 Gbit/s,实际还包含协议、key、请求和网络开销。容量评估要同时列 ops/s 与 bytes/s,不能因为 CPU 较低就判断仍有充足容量。
命中率需要按请求量加权。冷门对象数量很多,但请求很少;一个热点活动占 60% 读取时,它的命中状态决定数据库回源。整体 95% 命中率可能掩盖关键接口或热点 key 的突然失效。应计算:
db_read_qps = cacheable_read_qps × (1 - hit_ratio)
100,000 × (1 - 95%) = 5,000 QPS
100,000 × (1 - 80%) = 20,000 QPS
命中率只下降 15 个百分点,回源量增加四倍。缓存容量计划要验证冷启动、批量过期和节点故障下的回源峰值。Redis 的延迟监控文档也提醒慢命令、持久化与过期等内部事件会制造延迟尖峰;平均 ops/s 无法覆盖这些尾延迟。
八、消息队列容量要用“多久追平积压”衡量
队列入口每秒生产 P 条,消费者每秒完成 C 条。当 P > C 时,积压以 P - C 的速度增长:
backlog_after_t = initial_backlog + (P - C) × t
活动开放十秒,生产 4 万条每秒,消费者处理 1.5 万条每秒,会新增 25 万条积压。活动结束后生产下降到 2,000 条每秒,消费者仍完成 1.5 万条每秒,净消化速度为 1.3 万条每秒,大约 19.2 秒才能清空。若产品要求 10 秒内给出报名结果,这个消费者容量不合格;若队列只负责短信通知,几十秒可能可以接受。
积压条数还应换算为最老消息年龄。不同消息大小、分区和处理成本下,同样十万条的恢复时间差异很大。Apache Kafka 的监控文档列出生产与消费速率、每分区 lag 和 records-lag-max 等指标。实际容量看最慢分区,因为某个热点 key 固定进入一个分区时,其他分区空闲也不能替它保持顺序。
分区数决定最大消费并行度,但增加分区也会增加 broker 元数据、文件、复制和重新分配成本。批量消费能降低每条消息固定开销,却会增加等待凑批的延迟,并扩大单批失败后的重试工作量。Kafka 的设计文档明确把批处理描述为以少量额外延迟换取吞吐;容量计划需要写出 batch size、等待时间和单条处理成本。
九、网络和外部依赖也要进入预算
QPS 相同,1 KiB JSON 与 5 MiB 文件的网络成本相差数千倍。网络预算至少包括入站、出站、跨可用区、服务间放大、复制和备份。简化计算如下:
bandwidth = requests_per_second × bytes_per_request × direction_factor
20,000 × 8 KiB × 2 ≈ 312.5 MiB/s
direction_factor = 2 只是把请求和响应粗略合并,真实模型要分别计算大小。若网关调用三个下游,跨区复制两份,公网出口还要单独计费,入口字节会被多次搬运。压缩可以减少带宽,却增加 CPU 和延迟,也要通过实测决定是否划算。
第三方 API 的合同配额、连接上限和响应时间属于硬容量边界。短信供应商只能处理 5,000 条每秒时,内部消费者扩到 50,000 条每秒没有意义。可以通过队列平滑发送,但积压年龄必须满足通知时效;多个供应商切换还要预留各自凭证、配额和故障时的降级行为。
十、把冗余、发布和故障写进实例数
通过 峰值 QPS ÷ 单实例 QPS 得到的只是理论基础实例数。生产预算还要考虑目标利用率、负载不均、滚动发布、机器故障和可用区故障。假设目标峰值 39,000 QPS,压测得到每实例在目标 p99 下可持续 1,500 QPS,目标利用率 60%:
base_instances = ceil(39,000 / 1,500) = 26
with_headroom = ceil(26 / 0.60) = 44
若滚动发布最多同时不可用 2 台,另要求承受 1 台意外故障,可以配置至少 47 台。若要求失去一个承载 40% 流量的可用区后仍满足 SLO,预算要按剩余 60% 容量反推,结果会高于简单的 +3。
Google SRE 的生产服务实践建议容量覆盖同时发生的计划内与计划外下线,并通过负载测试校准单实例能力。另一篇生产环境案例展示了从约 3,470 QPS、每任务约 100 QPS 推导至少 35 个任务,再按 N+2 配置 37 个任务的例子。这里可复用的是推导方式,具体冗余模型仍取决于故障域和业务 SLO。
余量需要指定用途。发布余量、故障余量、突发余量和增长余量可以共享一部分物理资源,但不能在计划中被重复计算。例如团队已经用 40% 空闲容量覆盖单可用区故障,就不能又声称同一份 40% 专门用于增长且永远可用。
十一、自动扩缩容不能替代预留容量
自动扩容是一条有延迟的控制回路:采集指标、聚合、作出决策、调度 Pod、拉取镜像、启动进程、预热缓存、通过 readiness,之后新实例才开始提供容量。若尖峰在五秒内到达,而完整扩容需要一分钟,事故发生时新增实例仍在启动。
Kubernetes HPA 文档说明控制器周期性读取 CPU、内存或自定义指标,并按当前值与目标值的比例计算副本数;默认控制循环并非连续执行,未就绪 Pod 和缺失指标也会影响计算。容量计划因此要记录:
minReplicas能否承接扩容生效前的流量;- 使用 CPU、QPS、在途并发还是队列长度作为扩容信号;
- 从发出扩容决定到实例实际 Ready 的分阶段耗时;
- 单次最大扩容速度、资源配额和节点供应是否足够;
- 缩容稳定窗口是否会误删仍在处理长请求的实例。
CPU 指标适合 CPU 成为瓶颈且请求成本稳定的服务。I/O 密集服务可能在 CPU 较低时连接池已经占满,消费者则更适合依据积压年龄和处理速率扩容。预测明确的活动应提前扩容并完成缓存预热,HPA 负责吸收预测误差和日常波动。
十二、压测负责把假设换成实测系数
容量表一开始包含大量假设:单请求 CPU、缓存命中率、SQL 延迟、每实例吞吐、扩容时间。压测的目标是替换这些系数,并找到第一个违反 SLO 或不变量的边界。下一篇会专门讨论瓶颈定位,这里先定义容量验收需要什么实验。
测试应逐级增加负载,直到经过目标、拐点和过载区,而不是在目标 QPS 成功一次就停止。每个阶段保持足够时间,使连接池、GC、缓存、复制、队列和磁盘进入稳定状态。至少记录:
输入:各接口 QPS、并发、请求大小、key 分布、重试比例
输出:完成吞吐、错误率、p50/p95/p99、业务结果分布
资源:CPU、内存、GC、网络、连接、锁、IOPS、磁盘延迟
排队:入口队列、线程池等待、连接池等待、消息 lag 与年龄
正确性:超发、重复报名、消息丢失、读到旧状态的窗口
避免 coordinated omission 美化延迟
闭环压测器常在收到上一个响应后才发送下一个请求。服务变慢时,压测器也跟着减速,本该到达的请求没有发出,测得的延迟和压力同时降低,这就是 coordinated omission 的常见表现。开环模型按预定到达率发请求,更接近外部用户不会因为服务变慢就自动减少到达的场景。
HdrHistogram提供按预期间隔校正记录的能力,用于补偿慢响应期间缺失的样本。工具不能替代正确的流量模型:测试仍需限制压测机自身 CPU 和网络、记录实际发送率与完成率,并确认生成器没有先成为瓶颈。
峰值、热点、依赖变慢和恢复积压要分别测试。均匀流量的最大 QPS 无法说明单热点活动的容量;稳定数据库无法说明连接等待如何传播;只测故障发生、不测恢复,则看不到积压回放造成的第二次峰值。
十三、用一次完整计算检查活动报名系统
下面把前文数字组合成一份简化容量表。所有实测系数都应由目标版本的压测替换,这里只演示计算关系。
入口与应用
报名峰值:39,000 QPS(10 秒窗口)
详情读取:80,000 QPS
结果查询:15,000 QPS
报名应用:1,500 QPS / instance @ p99 < 1s
目标利用率:60%
基础实例:ceil(39,000 / 1,500 / 0.60) = 44
发布与单机故障余量:+3,合计 47
详情读取和报名写入成本不同,应使用独立实例组或至少使用不同加权系数。若混部后只按总 QPS 扩容,便宜读取会掩盖昂贵写入的资源需求。
数据库与缓存
假设 30% 报名请求被幂等层识别为重复,剩余请求平均执行 3 次 SQL:
DB operations ≈ 39,000 × 70% × 3 = 81,900 ops/s
这不意味着一个数据库要完成 81,900 个独立事务。需要按 SQL 类型拆分,利用唯一约束与条件更新合并步骤,并通过压测得到热点活动下的事务吞吐。如果单热点库存只能完成 8,000 次条件更新每秒,入口必须排队、分桶或拒绝,增加应用实例不会改变它。
活动详情 80,000 QPS,缓存命中率目标 98%,正常回源约 1,600 QPS。缓存节点故障或热点 key 失效时,最坏回源可能接近 80,000 QPS,因此要做预热、请求合并和回源限流。数据库日常读取容量不应被误认为能接住完整缓存流量。
消息与恢复
每个确认报名产生一条通知消息。假设最终确认 10 万人,开放十秒内生产完成,消费者稳定处理 5,000 条每秒:
平均生产速率 = 100,000 / 10s = 10,000 msg/s
十秒新增积压 = (10,000 - 5,000) × 10 = 50,000
活动后清空时间 = 50,000 / 5,000 = 10s
通知在约二十秒内完成,若产品接受一分钟内到达,这份预算可行。消费者停机五分钟后恢复时还要计算历史积压,并给在线报名事件保留优先级,避免旧通知占满所有消费能力。
结论与缺口
这份表仍有三个关键未知量:单热点库存更新上限、各类报名请求的真实 CPU 时间、实例从扩容到 Ready 的时间。它们不能靠继续估算解决,应进入压测和启动演练。容量评估的价值就在这里:把“系统大概能扛住”改写为待验证的系数和明确的薄弱点。
十四、让容量计划成为持续更新的工程资产
容量计划会随代码、数据和业务一起失效。新增日志、改变序列化、增加索引、调整缓存 TTL、升级运行时或修改重试策略,都可能改变资源系数。Google SRE 的生产服务实践明确建议用负载测试而非历史惯例确定资源与 QPS 的关系,并持续对比预测与真实需求。
可以把容量表纳入版本库,至少记录:
| 字段 | 示例 |
|---|---|
| 适用版本 | commit、镜像或发布版本 |
| 工作负载 | API 占比、key 分布、数据规模、请求大小 |
| 服务目标 | p99、错误率、持续时间、正确性约束 |
| 实测系数 | 每实例 QPS、CPU-ms/request、DB ops/request |
| 安全余量 | 目标利用率、发布和故障模型 |
| 预测窗口 | 当前峰值、增长率、计划活动、提前量 |
| 失效条件 | 哪些代码、数据或基础设施变化后要重测 |
| 负责人 | 谁更新预测、谁申请资源、谁批准降级方案 |
线上监控要能复算模型。每周比较预测峰值与实际峰值、单实例吞吐与压测结果、缓存命中与回源、消费能力与积压年龄。若实际每实例容量持续下降,即使总资源暂时足够,也说明软件效率或请求结构已变化。提前发现系数漂移,比在下一次活动前临时加机器可靠。
成本也应作为输出。目标利用率从 60% 降到 30% 可能近似翻倍计算资源,却换来更强的突发和故障承受能力;跨可用区冗余增加网络与副本成本,却减少单区故障影响。容量评审需要把这类交换写清楚,交给业务 SLO 和风险偏好决定。
十五、常见错误与一份可执行检查表
容量评估最常见的问题是模型遗漏,公式反而很少是最大障碍:
- 用日均 QPS 代替秒级峰值,没有描述流量形状;
- 把所有请求当成相同成本,忽略读写、命中和响应大小;
- 用平均延迟验收,错过尾延迟和排队拐点;
- 只算应用实例,没有计算数据库、缓存、队列和网络放大;
- 用集群平均利用率掩盖热点分片、热点行和最慢分区;
- 把自动扩容当成立即可得的容量,没有计算观测和启动延迟;
- 容量刚好覆盖正常峰值,没有发布、故障和预测误差余量;
- 压测只跑正常区,没有越过拐点,也没有测试恢复积压;
- 保存最终机器数,却没有保存版本、工作负载和实测系数。
一次容量评审可以按下面顺序完成:
1. 写清接口、业务事件、峰值窗口和 key 分布
2. 把用户动作展开为 RPC、SQL、缓存和消息倍率
3. 用 Little's Law 估算各阶段在途并发
4. 分别计算 CPU、内存、连接、网络、存储与 I/O
5. 为数据库热点、缓存回源和消息积压建立独立上限
6. 把目标利用率、发布与故障域加入基础容量
7. 计算自动扩容完整生效时间并设置最低预留
8. 通过开环压测校准系数,找到延迟与吞吐拐点
9. 验证过载拒绝、降级和恢复阶段仍然有界
10. 保存版本化容量表,并用线上指标持续复算
完成这些步骤后,“需要多少机器”只是计算结果之一。更有价值的信息是系统首先会在哪里到达边界,预测错了以后怎样保护核心路径,失去一部分资源时还能提供什么服务,以及下一次代码或流量变化后哪些系数必须重测。下一篇将继续沿这份容量模型进入性能优化与压测,重点讨论怎样用证据定位真实瓶颈。
参考资料
- John D. C. Little:A Proof for the Queuing Formula: L = λW
- Google SRE:Demand Forecasting and Capacity Planning
- Google SRE:Production Services Best Practices
- Google SRE:A Production Environment at Google
- Kubernetes:Horizontal Pod Autoscaling
- Redis:Redis benchmark
- Redis:Latency monitoring
- Apache Kafka:Monitoring
- Apache Kafka:Design
- HdrHistogram:A High Dynamic Range Histogram
如果这篇文章对你有帮助