惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
W
WeLiveSecurity
P
Proofpoint News Feed
月光博客
月光博客
NISL@THU
NISL@THU
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
T
Threatpost
Y
Y Combinator Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Vercel News
Vercel News
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
小众软件
小众软件
MyScale Blog
MyScale Blog
N
News and Events Feed by Topic
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
The Hacker News
The Hacker News
Schneier on Security
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Help Net Security
Help Net Security
Recent Announcements
Recent Announcements
S
Security @ Cisco Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Securelist
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
雷峰网
雷峰网
量子位
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
I
Intezer
T
The Blog of Author Tim Ferriss
G
GRAHAM CLULEY
D
DataBreaches.Net
V
Vulnerabilities – Threatpost
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
罗磊的独立博客

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 【Linux 网络子系统深度拆解】内核网络调优方法论:从基准测试到生产验证 排序基准测试:用数据说话
【系统架构设计】容量规划:从拍脑袋到数据驱动
Liao Tonglang · 2026-04-13 · via 土法炼钢兴趣小组的算法知识备份

大促前两周,运维问架构师:“这次需要加多少台机器?”架构师打开去年大促的监控数据看了看,说:“去年 100 台顶住了,今年流量预计翻倍,加到 200 台吧。”运维追问:“内存要多大?磁盘要 SSD 还是 HDD?数据库连接池开多少?”架构师沉默了三秒,说:“先按去年配置来,不够了再加。”

大促当天,应用服务器 CPU 使用率只有 40%,但数据库连接池打满了——200 台应用服务器同时向数据库发起连接,远超数据库的最大连接数。紧急扩容数据库连接数后,数据库 CPU 飙到 95%,因为连接数增加导致并发查询暴增。最终靠临时关闭非核心功能才撑过峰值。

这个场景的根源不是”机器不够”,而是没有容量模型。容量规划(Capacity Planning)不是”估一下要多少台机器”,而是一个从需求预测到资源建模到验证反馈的完整工程过程。

本文要回答三个问题:

  1. 排队论(Queueing Theory)在容量规划中怎么用?Little 定律和 M/M/c 模型能给出什么?
  2. 容量基线(Baseline)和水位线(Waterline)怎么建、怎么管?
  3. 全链路压测怎么设计,才能真正验证容量模型而不只是”跑个数”?

上一篇中,我们讨论了 SLO 工程——如何量化管理系统的可靠性。容量规划是 SLO 的前置条件:如果容量不足,再好的 SLO 也无法达标。


一、为什么”加机器”不是容量规划

“流量涨了就加机器”——这个策略在简单场景下有效,但随着系统复杂度增加,它的局限性会迅速暴露。

瓶颈不在你以为的地方

一个典型的请求链路涉及多个组件:负载均衡 → API 网关 → 应用服务 → 缓存 → 数据库 → 消息队列。每个组件都有自己的容量上限,系统整体容量取决于最窄的那个瓶颈。

你给应用服务加了 10 台机器,但瓶颈在数据库连接池(最多 500 个连接),那 10 台新机器只会让更多请求排队等待数据库连接,延迟反而增加。

资源之间的耦合

CPU、内存、磁盘 I/O、网络带宽之间不是独立的。增加并发线程数可以提高 CPU 利用率,但也会增加内存占用和上下文切换开销。当内存不足时,操作系统开始使用 Swap,磁盘 I/O 急剧增加,CPU 花在 I/O 等待上的时间增多,吞吐量反而下降。

非线性扩展

大多数系统的性能不会随资源线性增长。根据 Amdahl 定律(阿姆达尔定律):

加速比 = 1 / ((1 - P) + P / N)

P = 可并行化的比例
N = 处理器数量

如果一个系统 80% 的工作可以并行化(P = 0.8),那么即使你加无限台机器,理论最大加速比也只有 1 / (1 - 0.8) = 5 倍。剩余 20% 的串行部分(数据库写锁、分布式锁、全局序号生成)决定了系统的扩展上限。

USL 模型

Amdahl 定律假设并行部分没有协调开销,但实际系统中并行工作者之间需要通信和同步。Neil Gunther 提出的通用可扩展性定律(Universal Scalability Law,USL)在 Amdahl 定律基础上增加了”串扰”(Crosstalk)参数:

C(N) = N / (1 + α(N-1) + β·N·(N-1))

N = 节点数
α = 串行化参数(Amdahl 定律中的非并行部分)
β = 串扰参数(节点间通信开销)
C(N) = 相对吞吐量

当 β > 0 时,增加节点数到一定程度后,吞吐量不仅不增长,还会下降——因为节点间协调的开销超过了新增节点带来的收益。这就是为什么某些系统在扩容后性能反而变差。


二、排队论基础:Little 定律与 M/M/c 模型

排队论(Queueing Theory)是容量规划的数学基础。它不能精确预测系统在真实负载下的表现,但能提供一个方向性正确的分析框架。

Little 定律

Little 定律是排队论中最基本、最实用的结论:

L = λ × W

L = 系统中的平均请求数(正在处理 + 排队等待的)
λ = 到达速率(每秒到达的请求数,即 QPS)
W = 平均逗留时间(从请求到达到处理完成的总时间)

这个公式看似简单,但适用范围极广——它不依赖任何关于到达分布或服务时间分布的假设。

应用场景一:估算并发连接数。

假设一个 Web 服务的 QPS = 1000,平均响应时间 = 200 毫秒。

L = 1000 × 0.2 = 200

系统中平均有 200 个并发请求。这意味着至少需要 200 个工作线程(或连接)来处理这些请求。如果线程池大小设为 100,那一半的请求必须排队等待,延迟会显著增加。

应用场景二:评估扩容效果。

如果通过优化数据库查询,把平均响应时间从 200 毫秒降到 100 毫秒,相同的 QPS 下并发请求数降为:

L = 1000 × 0.1 = 100

不需要加机器,只需要优化代码,就把资源需求减半了。

M/M/c 模型

M/M/c 模型是排队论中用于分析多服务台(Multi-Server)系统的经典模型。

M = 到达过程服从泊松分布(Poisson Distribution)
M = 服务时间服从指数分布(Exponential Distribution)
c = 服务台数量(即可并行处理请求的工作者数量)

核心参数:

ρ = λ / (c × μ) = 系统利用率

λ = 到达速率
μ = 单个服务台的服务速率(1 / 平均服务时间)
c = 服务台数量

当 ρ 接近 1 时,队列长度趋向无穷大,延迟急剧增加。这是容量规划中最重要的结论:不要让系统利用率接近 100%

平均响应时间
     ↑
     |                          /
     |                        /
     |                      /
     |                    /
     |                  /
     |                /
     |             /
     |          /
     |       /
     |    ──
     | ──
     +——————————————————————→ 利用率 ρ
     0%     50%     80% 90%   100%

M/M/c 模型给出了一个关键的工程建议:CPU 利用率的安全水位线应该控制在 60-75%。超过这个范围后,排队延迟会快速增长。

CPU 利用率 排队延迟增幅(相对于空闲时) 工程建议
30% 约 1.4x 安全,有充足余量
50% 约 2x 正常运行水位
70% 约 3.3x 接近水位线,准备扩容
80% 约 5x 超出水位线,应立即扩容
90% 约 10x 危险,延迟显著恶化
95% 约 20x 紧急,系统即将不可用

排队论的局限

排队论模型基于一系列理想假设(泊松到达、指数服务时间、无限队列长度),真实系统的行为往往偏离这些假设:

  1. 到达过程不是泊松分布。真实流量有突发性(Burstiness),尤其是大促开始瞬间的流量尖峰,比泊松分布预测的极端情况更极端。
  2. 服务时间不是指数分布。数据库查询的响应时间通常是双峰分布——缓存命中时很快,缓存未命中时很慢。
  3. 队列不是无限的。线程池有大小限制,连接池有上限,超出限制的请求会被拒绝而不是排队。

因此,排队论应该用来做”方向性判断”(这个配置大概能不能扛住这个负载),不能用来做”精确预测”。精确预测需要全链路压测。


三、资源建模:CPU、内存、I/O、网络

容量规划需要对每种资源分别建模,然后找出最先触达瓶颈的资源——它决定了系统的整体容量上限。

CPU 建模

CPU 需求 = 每个请求的 CPU 时间 × 请求速率
CPU 容量 = CPU 核心数 × (1 - 安全余量)
最大 QPS = CPU 容量 / 每个请求的 CPU 时间

例如:一台 8 核服务器,每个请求平均消耗 4 毫秒的 CPU 时间,安全余量 30%。

CPU 容量 = 8 × (1 - 0.3) = 5.6 核
最大 QPS = 5600 毫秒/秒 / 4 毫秒/请求 = 1400 请求/秒

注意:这里的”CPU 时间”是纯 CPU 计算时间,不包括 I/O 等待。如果一个请求的总响应时间是 100 毫秒但 CPU 时间只有 4 毫秒,说明 96 毫秒花在了 I/O 等待上——这种情况下 CPU 通常不是瓶颈。

内存建模

内存需求 = 基础内存 + 每连接内存 × 并发连接数 + 缓存大小

基础内存 = JVM 堆 + 操作系统 + 其他常驻进程
每连接内存 = 请求缓冲区 + 线程栈 + 上下文数据

Java 应用的典型内存构成:

  • JVM 堆(Heap):4-8 GB。
  • JVM 非堆(Metaspace、Code Cache 等):256-512 MB。
  • 每个线程栈:512 KB-1 MB。200 个线程 = 100-200 MB。
  • 操作系统和其他开销:500 MB-1 GB。
  • 总计:单个实例需要 6-10 GB 内存。

磁盘 I/O 建模

IOPS 需求 = 每个请求的 I/O 操作数 × 请求速率
IOPS 容量 = 磁盘的 IOPS 上限

HDD:约 100-200 IOPS(随机读写)
SSD:约 10000-100000 IOPS
NVMe SSD:约 100000-1000000 IOPS

如果每个数据库查询平均产生 5 次随机磁盘读取,QPS = 1000 时需要 5000 IOPS。HDD 无法满足,必须使用 SSD。

网络建模

带宽需求 = 每个请求的数据量 × 请求速率

入方向:请求体大小 × QPS
出方向:响应体大小 × QPS

如果平均响应体大小 = 10 KB,QPS = 10000:

出方向带宽 = 10 KB × 10000 = 100 MB/s = 800 Mbps

一根千兆网卡(1 Gbps)在 80% 利用率时只能提供 800 Mbps,刚好触达上限。这种情况下需要万兆网卡或多网卡绑定。

综合建模

把各维度的容量上限汇总,找出最低的那个——它就是系统的瓶颈。

graph TD
    subgraph 资源容量模型
        CPU["CPU<br/>单机最大 QPS: 1400"]
        MEM["内存<br/>单机最大连接: 200"]
        DISK["磁盘 I/O<br/>单机最大 QPS: 2000<br/>(SSD)"]
        NET["网络<br/>单机最大 QPS: 12500<br/>(万兆网卡)"]
    end

    CPU --> BN["瓶颈分析"]
    MEM --> BN
    DISK --> BN
    NET --> BN

    BN --> RESULT["系统瓶颈: CPU<br/>单机最大 QPS = 1400<br/>目标 QPS = 10000<br/>需要 8 台应用服务器"]

四、需求预测:从历史数据到业务增长

容量规划的输入是”未来需要多少容量”。这个预测来自两个方向:历史趋势外推业务增长预期

历史趋势外推

收集过去 3-12 个月的流量数据(QPS、DAU、订单量等),拟合增长曲线,外推到目标时间点。

常见的增长模型:

  • 线性增长:y = a + bx。适用于成熟期的稳定业务。
  • 指数增长:y = a × e^(bx)。适用于增长期的业务。
  • 对数增长:y = a + b × ln(x)。适用于接近饱和的业务。
QPS
  ↑
  |            ○ ○ ○   ← 线性外推
  |         ○
  |       ○
  |     ○
  |   ○
  |  ○
  | ○
  +——————————————→ 时间
     历史数据    预测

注意:历史趋势外推假设未来的增长模式与过去相同。如果业务计划有大的变化(例如新增一个大客户、开拓新市场),历史趋势就不准了。

业务增长预期

与产品和业务团队对齐未来的业务计划:

  • 预计新增多少用户?
  • 是否有大型营销活动或促销?
  • 是否计划开拓新地域/新市场?
  • 是否有新功能会改变用户行为(例如一个新的视频功能会显著增加带宽需求)?

把业务预期转化为技术指标:

预计新增 100 万 DAU(日活跃用户)
→ 平均每用户每日请求 50 次
→ 日增请求量 5000 万
→ 峰值系数 3x(峰值 QPS ≈ 平均 QPS × 3)
→ 峰值 QPS 增量 = 5000 万 / 86400 × 3 ≈ 1736 QPS

峰值估算

容量规划必须基于峰值而非平均值。峰值 / 平均值的比率(Peak-to-Average Ratio)因业务类型而异:

业务类型 峰均比 说明
企业 SaaS 2-3x 工作日白天是峰值,夜间和周末是低谷
社交媒体 3-5x 午间和晚间峰值明显
电商(日常) 3-5x 晚间购物高峰
电商(大促) 10-50x 大促开始瞬间流量尖峰
游戏 5-10x 新版本上线、节假日

大促场景下的峰值估算尤其困难。2019 年天猫双十一零点峰值达到 54.4 万笔/秒,是日常峰值的数十倍。这种量级的尖峰,历史趋势外推几乎无法预测,必须结合业务部门的营销计划和流量预估。


五、容量基线与水位线管理

容量基线

容量基线(Capacity Baseline)是系统在标准负载下各项资源指标的正常范围。它是容量管理的”参照物”。

建立基线的步骤:

  1. 选择一个代表性的时间段(通常是最近 4-8 周的工作日)。
  2. 排除异常数据点(故障期间、大促期间)。
  3. 计算各指标的统计分布:p50、p90、p99、最大值。
  4. 将基线文档化,并与团队共享。

基线指标示例:

┌────────────────────────────────────────────────┐
│ 容量基线 — 订单服务 — 2025 Q4                      │
├────────────────┬──────┬──────┬──────┬──────────┤
│ 指标            │ p50  │ p90  │ p99  │ 峰值      │
├────────────────┼──────┼──────┼──────┼──────────┤
│ QPS            │ 800  │ 1200 │ 1500 │ 2000     │
│ CPU 利用率(%)   │ 35   │ 50   │ 65   │ 78       │
│ 内存利用率(%)   │ 60   │ 65   │ 70   │ 75       │
│ DB 连接池利用率 │ 30   │ 45   │ 60   │ 72       │
│ p99 延迟(ms)   │ 120  │ 180  │ 350  │ 800      │
└────────────────┴──────┴──────┴──────┴──────────┘

水位线

水位线(Waterline)是各资源指标的告警阈值,分为多个级别:

级别 CPU 利用率 内存利用率 DB 连接池 动作
绿色 < 50% < 60% < 50% 正常运行
黄色 50-70% 60-75% 50-70% 关注,制定扩容计划
橙色 70-80% 75-85% 70-85% 告警,启动扩容流程
红色 > 80% > 85% > 85% 紧急,立即扩容或降级

水位线的设定要考虑扩容的提前量(Lead Time)。如果从决定扩容到新资源上线需要 2 小时(包括审批、采购、部署、预热),那么在达到橙色水位线时就应该开始扩容,不能等到红色才动手。

水位线与 SLO 的关系

水位线是保障 SLO 的”提前预警”。如果 CPU 持续在红色水位线运行,排队延迟会急剧增加,最终导致延迟 SLO 被突破。

flowchart LR
    A["资源水位监控"] --> B{"当前水位"}
    B -->|绿色| C["正常运行"]
    B -->|黄色| D["生成扩容报告<br/>排入下周计划"]
    B -->|橙色| E["触发扩容工单<br/>2小时内完成"]
    B -->|红色| F["紧急扩容<br/>+ 启动降级预案"]
    E --> G["扩容完成<br/>水位回落"]
    F --> G
    G --> A

六、全链路压测

容量模型给出了理论预测,全链路压测(Full-Chain Load Testing)是验证这个预测的唯一手段。

什么是全链路压测

与传统的单接口压测不同,全链路压测模拟真实用户的完整行为链路——从浏览商品、加入购物车、提交订单到完成支付——按照真实的流量比例同时施压。

传统压测:只压一个接口
  压测工具 ──→ 搜索 API ──→ 看搜索 API 扛多少 QPS

全链路压测:按真实比例压所有接口
  压测工具 ──→ 搜索 API (50%)
            ──→ 详情 API (30%)
            ──→ 下单 API (15%)
            ──→ 支付 API (5%)

压测环境

全链路压测最理想的方式是在生产环境做。原因是:

  1. 测试环境的硬件配置、网络拓扑、数据量、中间件版本与生产环境不同,压测结果不可信。
  2. 测试环境的数据量通常远小于生产环境,数据库查询在小数据量下的表现与大数据量下完全不同。

在生产环境做压测需要解决两个关键问题:

流量隔离:压测流量必须与正常用户流量隔离,不能影响真实用户。常见方案是在请求头中添加压测标识(如 X-Test: shadow),中间件根据标识把压测请求路由到影子表/影子库。

数据隔离:压测产生的数据(订单、支付记录等)不能混入正常业务数据。解决方案包括影子数据库(Shadow Database)、影子表(Shadow Table)或压测结束后清理数据。

压测流量模型设计

压测流量模型需要尽可能贴近真实流量的特征:

  1. 请求比例:各接口的请求比例应与真实流量一致。
  2. 用户行为序列:用户不是随机访问接口的,而是按照一定的路径(浏览 → 加购 → 下单 → 支付)。
  3. 数据分布:热门商品和冷门商品的访问比例、大额订单和小额订单的比例。
  4. 时间模式:流量的递增模式。大促流量不是匀速增长的,而是在开始瞬间有一个陡峭的尖峰。

压测指标收集

压测过程中需要监控的核心指标:

┌──────────────────────────────────────────────────┐
│                全链路压测监控面板                      │
├────────────────┬─────────────────────────────────┤
│ 业务指标        │ QPS、成功率、错误类型分布             │
│ 延迟指标        │ p50、p90、p99、max                 │
│ 应用指标        │ CPU、内存、GC 次数/时长、线程数       │
│ 数据库指标      │ 连接数、QPS、慢查询数、锁等待          │
│ 缓存指标        │ 命中率、内存使用、连接数               │
│ 消息队列指标    │ 积压量、消费速率、延迟                  │
│ 网络指标        │ 带宽、连接数、TCP 重传率               │
└────────────────┴─────────────────────────────────┘

压测分析与调优

压测结果的分析不是”看 QPS 够不够”,而是找出系统在压力下的行为模式:

  1. 瓶颈在哪? 哪个组件最先达到容量上限?
  2. 劣化曲线是什么形状? 性能是线性下降还是突然崩塌?
  3. 恢复能力如何? 流量回落后,系统能否快速恢复到正常水平?

七、容量规划流程

把上述方法论整合为一个可执行的流程:

度量 → 建模 → 预测 → 供给 → 验证 → 反馈

1. 度量:收集当前系统的性能基线数据
2. 建模:构建资源消耗模型(CPU/内存/IO/网络)
3. 预测:结合业务增长预期,计算未来资源需求
4. 供给:采购/申请/预留资源
5. 验证:通过全链路压测验证预测的准确性
6. 反馈:用压测结果修正模型,迭代优化

时间线

时间节点 动作 产出
T-90 天 收集历史数据,构建容量模型 容量模型文档
T-60 天 与业务团队对齐增长预期,计算资源需求 资源需求清单
T-45 天 提交资源申请,启动采购 采购订单
T-30 天 资源到位,部署并加入集群 扩容完成报告
T-14 天 第一轮全链路压测 压测报告、问题清单
T-7 天 修复问题后第二轮压测 验证报告
T-3 天 最终检查,确认所有预案就位 上线 Checklist
T-0 大促 / 上线 实时监控
T+7 天 复盘,修正容量模型 复盘报告

八、工程案例:某电商平台的大促容量规划

以下案例基于国内某大型电商平台公开的技术分享,展示了一次双十一大促的容量规划全过程。

背景

  • 日常峰值 QPS:约 5 万。
  • 大促目标峰值 QPS:50 万(10 倍于日常)。
  • 核心链路:搜索 → 详情 → 加购 → 下单 → 支付。
  • SLO 要求:可用性 >= 99.95%,下单 API p99 延迟 <= 1 秒。

容量模型

第一步:度量当前基线。

在日常峰值 5 万 QPS 下:

  • 应用服务器 40 台(每台最大承载 1800 QPS,当前负载约 1250 QPS)。
  • MySQL 主从集群,主节点 CPU 利用率 45%,连接数约 2000。
  • Redis 集群 6 节点,内存使用率 55%,QPS 约 15 万。

第二步:资源需求预测。

目标 QPS = 50 万,是日常的 10 倍。但不是所有组件都需要线性扩容 10 倍:

  • 应用服务器:线性扩容。50 万 / 1800 = 278 台,加上 30% 安全余量 = 362 台。
  • Redis:QPS 线性增长到 150 万。需要 6 × 10 = 60 节点。但内存不需要 10 倍——热点数据是有限的。预估需要 40 节点。
  • MySQL:不能线性扩容。数据库的瓶颈是写入和锁竞争,不是纯粹的 QPS。解决方案是增加分库分表数量、引入读写分离、热点数据使用缓存挡住 80% 的读请求。

第三步:关键决策。

  • 数据库分库数从 8 扩到 64,每个库的连接数和写入量降为 1/8。
  • 引入本地缓存(Caffeine,本地缓存)+ Redis 二级缓存,将数据库读 QPS 降低 90%。
  • 订单创建使用异步化:前端先返回”订单创建中”,后台通过消息队列异步写入数据库。

全链路压测

第一轮压测(T-14 天):

压到目标 QPS 的 80%(40 万)时,发现以下问题:

  1. Redis 集群某些节点出现热点 Key(某爆款商品的库存 Key),单节点 CPU 打到 95%。
  2. 下单 API 的 p99 延迟飙到 3.2 秒,超过 1 秒的 SLO。瓶颈定位到分布式锁的竞争。
  3. 消息队列消费端出现积压,原因是消费者数量不足。

修复措施:

  1. 热点 Key 问题:引入本地缓存 + Key 分片策略(同一个逻辑 Key 分散到多个物理 Key)。
  2. 分布式锁竞争:把库存扣减改为基于 Redis Lua 脚本的原子操作,消除分布式锁。
  3. 消息队列积压:增加消费者实例数,从 16 个扩到 64 个。

第二轮压测(T-7 天):

成功压到 55 万 QPS(110% 目标值),所有 SLO 达标:

  • 可用性:99.97%。
  • 下单 API p99 延迟:780 毫秒。
  • MySQL 主节点 CPU 利用率:62%(安全水位内)。
  • Redis 集群最大节点 CPU 利用率:71%(接近黄色水位线但可接受)。

大促当天

实际峰值 QPS = 48 万,略低于预估的 50 万。所有指标在安全水位内,无重大故障。

复盘

  1. 应用服务器实际使用了 362 台中的 320 台(利用率约 88%),多余的 42 台是冗余。但考虑到安全余量,这个冗余是合理的。
  2. 容量模型对 Redis 的预测偏保守(预估 40 节点,实际 32 节点就够了),下次可以减少。
  3. 数据库的分库分表方案有效,但运维复杂度显著增加。建议长期迁移到分布式数据库。

九、容量规划的权衡

维度 过度规划(Over-Provisioning) 合理规划 不足规划(Under-Provisioning)
资源成本 高(大量闲置) 适中 低(但故障损失可能更高)
可用性风险
资源利用率 低(20-30%) 中(50-70%) 高但不稳定
规划投入 低(直接多买) 高(需要建模和压测) 低(但事后救火投入更高)
弹性应对 好(有余量吸收突发) 好(有计划性的弹性策略) 差(突发即过载)
适用场景 关键金融系统、不允许任何风险 大多数生产系统 早期创业、POC 验证

容量规划与云计算

云计算的弹性伸缩(Auto Scaling)能力部分缓解了容量规划的压力——不需要提前几个月采购硬件,可以按需扩缩。但云计算没有消除容量规划的需要:

  1. 弹性伸缩有延迟。从触发扩容到新实例可用,通常需要 2-5 分钟。大促开始瞬间的流量尖峰来不及扩容。
  2. 预留实例更便宜。按需实例(On-Demand)的价格通常是预留实例(Reserved Instance)的 2-3 倍。如果能预测基础用量,用预留实例 + 按需实例的组合可以显著降低成本。
  3. 数据层不容易弹性伸缩。数据库、缓存、消息队列的扩缩容涉及数据迁移和状态同步,不是简单地”加一台机器”就行。

十、结论

容量规划的本质是一个预测 → 验证 → 修正的迭代过程:

  1. 不要靠猜。用排队论和资源模型做方向性预测,用全链路压测做精确验证。
  2. 找瓶颈,不是堆资源。系统性能取决于最窄的瓶颈,堆资源到非瓶颈组件是浪费。
  3. 管水位线,不是看平均值。峰值决定容量需求,p99 比 p50 重要。
  4. 留余量,但不要过度。70% 的利用率水位线是一个合理的起点。
  5. 持续迭代。每次大促或重大发布后,用真实数据修正容量模型。

容量规划不是一次性项目,是一个持续运转的工程流程。做得好,它是护城河——让你在竞争对手被流量击垮时稳如磐石。做得差,它是隐形炸弹——在你最需要系统稳定的时候爆炸。


导航

上一篇:SLO 工程:可靠性的量化管理

下一篇:混沌工程:主动验证系统的韧性


参考资料

书籍

  • Neil J. Gunther, “Guerrilla Capacity Planning”, Springer, 2007. 容量规划领域的经典著作,详细推导了 USL 模型。
  • Neil J. Gunther, “Analyzing Computer System Performance with Perl::PDQ”, Springer, 2011. 排队论在系统性能分析中的应用。
  • John Allspaw, “The Art of Capacity Planning”, O’Reilly, 2008. 从 Flickr 的实践出发,讲解 Web 应用的容量规划方法论。
  • Martin Kleppmann, “Designing Data-Intensive Applications”, O’Reilly, 2017. 数据层扩展和分区策略的权威参考。

论文

  • John D.C. Little, “A Proof for the Queuing Formula: L = λW”, Operations Research, 1961. Little 定律的原始论文。
  • Gene M. Amdahl, “Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities”, AFIPS Conference Proceedings, 1967. Amdahl 定律的原始表述。

工业实践

  • 阿里巴巴技术博客, “全链路压测体系建设”, 2020. 双十一全链路压测的方法论和工程实践。
  • 美团技术博客, “美团点评大规模微服务容量规划实践”, 2021. 基于排队论的容量模型在美团的应用。
  • Google SRE Book, Chapter 18: “Software Engineering in SRE”. 容量规划与需求预测的自动化方法。

工具

  • Locust: GitHub 仓库(locustio/locust),基于 Python 的分布式负载测试工具。
  • k6: GitHub 仓库(grafana/k6),基于 Go 的现代化负载测试工具。
  • Gatling: GitHub 仓库(gatling/gatling),基于 Scala 的高性能负载测试工具。

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-13 · architecture

【系统架构设计】什么是软件架构:从代码结构到系统决策

软件架构的定义至少有三种主流理解,每种指向不同的关注点。本文对比 Booch、Fowler、Johnson 三家定义,分析架构决策的不可逆性特征,拆解 Krutchten 4+1 视图模型,回答一个核心问题:架构和设计的边界到底在哪里。

2026-04-13 · architecture

【系统架构设计】架构质量属性:不只是"高可用高性能"

需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。