7AA7 博客

容量规划与压测:如何估算系统承载能力

祝青禾 UID:121
2026-09-12 10:06
17 0 0

“这套系统能扛多少流量”很难凭直觉回答。难在两处:容量不是单一数字而是一组相互影响的参数;测算环境与真实环境总有差距,测出来的数不能直接搬用。把这两处理清楚,估算才有意义。

四个基本量与它们的换算

容量讨论里反复出现四个量:QPS、并发数、单次请求耗时、单机承载能力。它们之间有一条来自利特尔法则的核心关系:

并发数 ≈ QPS × 平均耗时

单位必须统一:耗时用毫秒而 QPS 用秒,算出的并发会差一千倍,这是估算里最常见的低级错误。

这条公式正反都能用:单机能同时处理 200 个并发、平均耗时 50ms,则 QPS 上限约 200 / 0.05 = 4000;目标 10000 QPS、平均耗时 50ms,需要的并发能力是 10000 × 0.05 = 500。

单机并发上限从哪来?CPU 密集场景可粗略从核数推:假设 8 核、每请求消耗 5ms CPU 时间,理想上限是 8 / 0.005 = 1600 QPS。这是理想值,上下文切换、垃圾回收、锁竞争都会吃掉一部分。它适合量级判断——”大概千级而不是万级”,不是精确预测。

量 单位 怎么得到 常见误区
QPS 请求/秒 网关或应用埋点统计 把峰值当作常态
并发数 个 网关活跃连接、线程池活跃数 与在线用户数混为一谈
耗时 毫秒 服务端处理时间,看分位数 只看平均值
单机承载 QPS 压测拐点前的稳定区间 用压测最大值当容量

木桶效应同样成立:CPU、内存、带宽、磁盘 IOPS、连接池、下游限流,任何一项先到上限,容量就停在那里。估算要逐一问过去,而不是只算 CPU。

压测的四种类型

压测不是”打满看看”,不同类型回答不同问题。

类型 加压方式 回答什么问题 停止条件
基准测试 单接口、固定并发 这个接口单机能到多少 指标稳定若干分钟
负载测试 从低到高逐级加压 性能曲线在哪出现拐点 出现明显拐点后再上一级
压力测试 超过预期峰值 崩溃点在哪、崩溃后能否恢复 错误率超标或不可恢复
稳定性测试 中等压力持续数小时以上 有没有泄漏、有没有缓慢劣化 达到预定时长

基准测试的意义是建立可比的数字:改动前后各跑一次看差异,条件是固定的——同样的并发、时长、数据分布。

负载测试看曲线形状而不是某个点:QPS 随并发上升,起初近似线性,到某点后增长放缓,再往后可能不升反降。拐点之后进入排队状态,延迟上升而吞吐不再增加,容量应取拐点之前。

压力测试观察崩溃形态:渐进劣化还是突然雪崩,撤压后能否自行恢复。稳定性测试暴露慢变量——内存增长、句柄泄漏、日志占满磁盘,两小时的压测看不出来。

压测环境为什么会骗人

压测环境与生产的差异是系统性的:

  • 数据量级不同:十万行与十亿行的查询计划不同,索引选择性和缓存命中率被数据量放大或缩小。
  • 依赖不同:下游沙箱或 mock 的延迟分布与真实不一致,而延迟分布恰恰决定排队行为。
  • 网络拓扑不同:少负载均衡、少一跳、同机房,延迟基数就不同;生产跨可用区的那一跳可能是毫秒级。
  • 配置不同:连接池、线程池队列、JVM 参数、CPU 型号,任何一项都会改变拐点位置。

结论是:压测值用于”发现拐点”和”比较版本差异”可靠,用于”直接宣布生产容量”不可靠。可行做法是维护环境差异清单,再结合灰度放量验证——先放 5% 真实流量看指标是否与推断一致,不一致就修正模型。

瓶颈定位的顺序

客户端(压测机)。压测机自身是否打满:CPU、可用端口、文件句柄、客户端 GC。压到上限的表现是”服务端很稳但 QPS 上不去”,判断方法是看压测机资源使用率,或换一台机器发压看结果是否变化。

网络。带宽、PPS、并发连接数;经过 NAT 网关或负载均衡时,连接数上限和空闲超时可能先成为瓶颈,连接复用与否在这里影响很大。

网关。限流规则是否生效、TLS 握手消耗的 CPU、上游连接是否复用。短连接场景下 TLS 握手的 CPU 开销常被低估。

应用。线程池队列积压、GC 停顿、锁竞争、连接池等待、慢依赖。最有效的证据是把队列等待与处理时间拆开——总耗时上升但处理时间不变,说明是排队。

数据库。慢查询、锁等待、IOPS、主从延迟、连接数。它既是瓶颈又是下游,扩容比无状态服务难得多。

为什么压测必须包含”慢”

只压”均匀的正常请求”会得到偏乐观的结论。真实流量里有慢请求:慢 SQL、慢下游、大报文、缓存未命中、冷启动。慢请求的危害不在自身,而在占用资源的时间更长——耗时从 10ms 变成 1s,占用连接和线程的时间是一百倍,排队因此放大。

排队论的直觉值得记住:资源利用率接近 100% 时,排队延迟不是线性增长而是急剧上升。系统看起来”还有余量”,实际已进入不稳定区——这也是容量要取拐点之前的原因。

压测脚本要显式构造慢场景:一定比例的缓存未命中、复杂查询、超大请求体、下游注入固定延迟。观察指标也要从平均耗时换成分位数——P99 和 P999 才是用户体验的真实边界,平均值会被大量快请求掩盖。

扩容为什么不是线性的

加了机器,容量未必按比例增长。非线性来源有几类:

  • 连接池:每个新实例都建立自己的下游连接,实例翻倍则连接数翻倍,数据库的连接上限会先到。
  • 共享资源:分布式锁、单线程协调组件、消息队列固定分区数、数据库写入能力,不会因为加法扩容而变强。分区数决定消费者的并行上限,扩容超过分区数的消费者是无效的。
  • 缓存:新实例是冷缓存,启动时回源压力集中,一批实例同时扩容可能造成回源风暴。
  • 下游配额:调用第三方有 QPS 配额,这个数字与实例数无关。
  • 长尾依赖:整体吞吐受限于最慢的那个依赖,扩容上游只是让请求更快堆积在它前面。

判断线性是否成立的办法是逐步扩容并观察:实例翻倍而吞吐只增 30%,说明瓶颈在别处,继续加机器只是增加成本。

余量与自动扩缩容阈值

容量要留余量:流量有波动、机器会故障、变更需要时间。常见做法是把目标利用率定在 50%~70%(经验值,取决于波动幅度与恢复速度)。

自动扩缩容的阈值设定有几处容易踩:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler    # 示意:字段以集群实际版本为准
spec:
  minReplicas: 4
  maxReplicas: 40
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 50
        periodSeconds: 30
    scaleDown:
      stabilizationWindowSeconds: 300

CPU 指标的局限是对 IO 密集型服务不敏感——请求卡在等下游,CPU 很低但已在排队。这类服务更适合用并发数、队列长度或自定义延迟作为依据。

阈值还要留出反应时间。扩容不是瞬间的:调度、拉镜像、启动、预热、通过就绪探针,加起来是分钟级。阈值若接近崩溃点,等触发时流量早已把系统压垮,应设在”从触发到生效这段时间内不会打穿”的水平。

缩容要更保守:缩得太快会在流量回落又回升时来回抖动,stabilizationWindowSeconds 就是给缩容一个观察窗口;缩容直接减少容量,风险大于扩容。

配置写完之后仍要验证:在压测中实际触发一次扩容,看它是否来得及、扩出来的实例是否真能承载。没验证过的扩缩容配置等同于没有。

441浏览量
17赞
0评论
0参与用户
9 分钟阅读时间

评论(0)

还没有评论,来抢沙发。