容量规划与弹性:水位线、扩缩容与大促保障
本文系统梳理容量规划与弹性架构的完整体系:从容量规划的三种方法论(历史数据外推/压测推算/排队论建模),到水位线三级告警体系,再到弹性伸缩的触发策略与 K8s HPA/KEDA 实战,以及大促前的容量保障流程。综合了《容量保障核心技术与实战》(吴骏龙)的核心内容,强调"鼓励快速扩容作为应急手段,但警惕无脑扩容"。
目录
| 章节 | 说明 |
|---|---|
| 容量保障的目标与度量 | 容量的定义与核心指标 |
| 容量规划三种方法 | 历史外推 / 压测推算 / 排队论 |
| 容量评估公式 | 峰值 QPS 推算、服务器数量计算 |
| 排队论:数学建模容量 | M/M/c 模型与互联网场景应用 |
| 水位线三级告警体系 | 安全水位 / 警戒水位 / 红线水位 |
| 容量治理三板斧 | 扩容 / 限流 / 降级的选择逻辑 |
| 弹性架构设计 | 无状态水平扩展 / 有状态分片扩展 |
| 弹性伸缩触发策略 | 基于指标 / 基于预测 / 基于规则 |
| K8s HPA/KEDA 弹性实战 | 配置示例与注意事项 |
| 大促容量保障流程 | 从 T-30 天到活动结束的完整时间线 |
容量保障的目标与度量
什么是容量
容量:系统在满足性能目标(RT、错误率)的前提下,能够处理的最大业务量。
注意:容量不是"系统能承受多少 QPS 而不崩溃",而是"在用户体验可接受的前提下,能处理多少请求"。
容量 = f(TPS 上限 | P99 RT < 目标值 && 错误率 < 目标值)
错误示例:系统在 10000 TPS 时不崩溃,但 P99 RT = 5s
→ 这不是容量,因为用户体验已不可接受
正确:系统在 5000 TPS 时 P99 RT = 200ms,错误率 0%
→ 5000 TPS 才是真正的容量
容量度量的 5 个经典问题
来自《容量保障核心技术与实战》(吴骏龙):
| 问题 | 反直觉的答案 |
|---|---|
| 响应时间越短越好吗? | 不一定。响应时间 1ms 和 5ms 对用户体验差异不大,但为了从 5ms 优化到 1ms 可能消耗大量资源。应关注 SLO 目标,而非无止境优化 |
| TPS 越高越好吗? | 不一定。TPS 提升但 RT 也在上升,说明系统已超负荷,这时的 TPS 是"虚高",不可持续 |
| 错误率为 0 才算通过? | 不一定。对于非核心接口,0.1% 的错误率是可接受的。目标应与业务 SLO 对齐 |
| 资源使用率越高越好? | 不一定。资源使用率 100% 意味着没有弹性空间,突发流量会立即崩溃。合理水位是 60-70% |
| 扩容能解决所有容量问题? | 不一定。代码中的串行瓶颈(数据库单点、全局锁)无法通过扩容解决,Amdahl 定律决定了上限 |
容量规划三种方法
方法一:历史数据外推法
适用场景:业务增长平稳,有足够历史数据
步骤:
1. 收集过去 3-12 个月的峰值 QPS 数据
2. 拟合增长曲线(线性/指数/季节性)
3. 预测未来 3-6 个月的峰值 QPS
4. 按峰值 × 安全系数(1.5-2x)配置容量
示例:
去年双11峰值:10000 TPS
今年业务增长预期:50%
预测今年双11峰值:15000 TPS
容量目标:15000 × 1.5(安全系数)= 22500 TPS
局限:无法预测突发业务增长(如病毒式传播、突发热点事件)。
方法二:压测推算法
适用场景:新系统上线前,或业务模式发生变化时
步骤:
1. 对单台服务器进行压测,得到单机 QPS 上限(在 P99 RT 目标内)
2. 计算所需服务器数量 = 峰值 QPS / 单机 QPS
3. 考虑资源利用率上限(通常 70%):
实际服务器数 = 峰值 QPS / (单机 QPS × 0.7)
示例:
单机压测结果:1000 TPS(P99 RT = 100ms)
预期峰值:5000 TPS
所需服务器数 = 5000 / (1000 × 0.7) ≈ 8 台
方法三:排队论建模法
最精确的方法,基于数学模型计算系统容量(详见下一节)。
容量评估公式
峰值 QPS 推算
日均请求量 = DAU × 人均请求次数
平均 QPS = 日均请求量 / 86400(秒)
峰值 QPS = 平均 QPS × 峰值倍数
峰值倍数参考:
普通互联网应用:3-5x
电商大促:10-20x(双11峰值可达日均 20 倍以上)
突发新闻事件:50-100x(不可预测)
设计容量 = 峰值 QPS × 安全系数(1.5-2x)
实际案例:
某电商平台:
DAU = 500 万
人均日请求次数 = 100 次
日均请求量 = 5 亿次
平均 QPS = 5亿 / 86400 ≈ 5787 QPS
双11峰值倍数 = 10x
峰值 QPS = 57870 QPS
设计容量 = 57870 × 1.5 ≈ 87000 QPS
服务器数量计算
单台服务器最大 TPS = 压测得到的单机容量 × 资源利用率上限(70%)
所需服务器数量 = 设计容量 / 单台最大 TPS
注意事项:
1. 压测环境与生产环境需配置一致
2. 单机容量要在与生产相同的数据量下测试
3. 考虑依赖服务(数据库、缓存)的容量上限
4. 预留 N+1 或 N+2 冗余(容灾需要)
磁盘容量评估
磁盘容量 = 原始数据量 + 索引量 + 日志量
= 原始容量 × 数据库膨胀因子(1.5-3x)× 备份因子(2-3x)
日志数据:
日均日志量 = 日均请求量 × 单条日志大小
保留期限 = 7-30 天
磁盘需求 = 日均日志量 × 保留天数 × 压缩比(0.1-0.3)
排队论:数学建模容量
基本概念
排队论将互联网服务抽象为排队系统:
- 到达者:用户请求(到达率 λ,单位:请求/秒)
- 服务台:服务线程/进程(服务率 μ,单位:请求/秒/线程)
- 队列:等待处理的请求缓冲区
M/M/c 模型
最常用的排队论模型,适用于互联网服务:
- M(Markovian):到达间隔服从指数分布(泊松过程)
- M:服务时间服从指数分布
- c:c 个并行服务台(线程数)
关键公式:
服务强度 ρ = λ / (c × μ) = 到达率 / (线程数 × 服务率)
当 ρ < 1 时:系统稳定,请求能被及时处理
当 ρ ≥ 1 时:系统过载,队列无限增长
平均等待时间 = f(ρ, c) — 随负载指数增长
实际应用示例:
场景:接口平均响应时间 20ms,当前 TPS = 500
服务率 μ = 1/0.02 = 50 请求/秒/线程
到达率 λ = 500 请求/秒
问:需要多少线程才能保证等待时间 < 5ms?
使用 M/M/c 模型计算:
最少需要 c = 12 个线程(ρ ≈ 0.83)
此时 99% 的请求等待时间 < 5ms
结论:线程池大小应设置为 15-20(留有余量)
排队论的局限性
| 局限 | 说明 |
|---|---|
| 假设不完全成立 | 真实请求到达不一定是泊松分布(秒杀时是突发) |
| 参数难以准确估计 | 服务时间分布在不同负载下会变化 |
| 忽略依赖关系 | 无法建模微服务间的复杂依赖 |
| 适用于稳态分析 | 无法分析突发峰值下的瞬态行为 |
结论:排队论提供理论下界,压测提供实际上限,两者结合才能做出可靠的容量规划。
水位线三级告警体系
水位线是容量管理的核心工具,通过定义三条线来管理系统的"健康状态":
graph TD
A["资源使用率"] --> B{"> 红线水位<br/>(如 90%)"}
B -->|是| C["🚨 紧急处置<br/>立即扩容/降级/限流<br/>值班人员立即响应"]
B -->|否| D{"> 警戒水位<br/>(如 75%)"}
D -->|是| E["⚠️ 告警通知<br/>准备扩容<br/>排查异常原因"]
D -->|否| F{"> 安全水位<br/>(如 60%)"}
F -->|是| G["ℹ️ 关注<br/>正常运行区间<br/>记录趋势"]
F -->|否| H["✅ 充裕<br/>资源利用率偏低<br/>考虑缩容"]
style C fill:#fcc,stroke:#c00
style E fill:#ffe,stroke:#aa0
style G fill:#cfc,stroke:#060
三级水位线定义
| 水位线 | 典型阈值 | 含义 | 响应动作 |
|---|---|---|---|
| 安全水位(Safe) | CPU 60% / 内存 70% | 正常运行区间,有充足弹性空间 | 无需操作,记录趋势 |
| 警戒水位(Warning) | CPU 75% / 内存 85% | 接近瓶颈,需要提前准备 | 告警通知,准备扩容方案 |
| 红线水位(Critical) | CPU 90% / 内存 90% | 系统已接近极限,随时可能崩溃 | 立即扩容/限流/降级 |
不同资源的水位线设置:
| 资源 | 安全水位 | 警戒水位 | 红线水位 |
|---|---|---|---|
| CPU 使用率 | < 60% | 60-75% | > 75% |
| 内存使用率 | < 70% | 70-85% | > 85% |
| 数据库连接池 | < 60% | 60-80% | > 80% |
| 磁盘使用率 | < 70% | 70-85% | > 85% |
| MQ 消息积压 | < 1万 | 1-10万 | > 10万 |
注意:水位线阈值需要根据业务特性调整。对于 CPU 密集型服务,安全水位可以设低一些(50%);对于 IO 密集型服务,CPU 70% 可能还是安全的。
容量治理三板斧
扩容:直接但要谨慎
原则:鼓励将快速扩容作为应急手段,但作为常态治理手段要谨慎,警惕无脑扩容。
扩容的正确姿势:
- 先分析是否是代码/配置问题(SQL 慢查询、连接池小、缓存命中率低)
- 如果是资源不足,扩容时同时关注依赖服务的容量(扩容应用但数据库是瓶颈,没用)
- 扩容后验证效果(TPS 是否线性增长,如果不是,说明有串行瓶颈)
扩容的边际递减效应:
当存在串行瓶颈时(如数据库单点),扩容效果递减:
1 台 → 2 台:TPS 从 1000 → 1800(+80%)
2 台 → 4 台:TPS 从 1800 → 2800(+55%)
4 台 → 8 台:TPS 从 2800 → 3600(+29%)
→ 瓶颈在数据库,继续扩应用服务没有意义
限流:保护核心,牺牲边缘
限流的目的:在资源有限时,保证核心业务的体验,主动拒绝超出容量的请求。
限流策略的选择:
| 策略 | 适用场景 |
|---|---|
| 全局限流 | 整体流量超出系统容量 |
| 接口级限流 | 某个接口异常消耗资源 |
| 用户级限流 | 防止单个用户/IP 刷接口 |
| 优先级限流 | 保核心接口,降级非核心接口 |
降级:有损但可用
降级的层次:
完整功能
↓ 一级降级:关闭非核心功能(推荐、评论、统计)
↓ 二级降级:返回缓存数据(可能过期)
↓ 三级降级:返回默认值/兜底数据
↓ 四级降级:服务完全不可用,返回友好错误页
三板斧的选择逻辑:
flowchart TD
A["容量告警"] --> B{"是代码/配置问题?"}
B -->|是| C["优化代码/配置(根本解决)"]
B -->|否| D{"是突发流量?"}
D -->|是| E["限流(保护系统)<br/>+ 扩容(增加容量)"]
D -->|否| F{"是持续增长?"}
F -->|是| G["扩容(长期方案)"]
F -->|否| H["降级(临时方案)<br/>+ 排查根因"]
弹性架构设计
无状态服务的水平扩展
设计原则:服务本身不存储状态,状态外置到存储层(数据库/缓存)。
无状态服务 = 任意实例可以处理任意请求
→ 可以随意增删实例,不影响正确性
→ 负载均衡可以随机路由
有状态服务(反例):
Session 存储在本地内存 → 扩容后 Session 丢失
本地缓存存储用户数据 → 不同实例看到不同数据
Session 外置方案:
// Spring Session + Redis:Session 存储在 Redis,而非本地内存
@EnableRedisHttpSession
public class SessionConfig {
// 所有实例共享同一个 Redis 中的 Session
// 任意实例崩溃,Session 不丢失
// 可以随意扩缩容
}
有状态服务的分片扩展
对于必须有状态的服务(如数据库、消息队列),通过分片实现水平扩展:
| 组件 | 分片方式 | 扩展方式 |
|---|---|---|
| MySQL | 分库分表(hash/range) | 预分片,迁移数据 |
| Redis | Redis Cluster(16384 slots) | 添加节点,自动迁移 slots |
| Kafka | 分区(Partition) | 增加分区数 |
| Elasticsearch | 分片(Shard) | 添加节点,自动均衡 |
弹性伸缩触发策略
三种触发策略
| 策略 | 触发条件 | 优点 | 缺点 |
|---|---|---|---|
| 基于指标 | CPU/内存/QPS 超过阈值 | 简单,反应准确 | 滞后(指标超标才扩容,此时已影响用户) |
| 基于预测 | 历史规律预测未来负载 | 提前扩容,用户无感知 | 预测不准时浪费资源 |
| 基于规则 | 定时扩容(如每天 9:00) | 简单可控 | 需要人工维护规则 |
最佳实践:三种策略组合使用:
- 定时规则:大促前提前扩容(T-2 小时)
- 预测伸缩:根据历史流量趋势提前 15-30 分钟扩容
- 指标触发:兜底,当以上两种未能覆盖时快速响应
扩容滞后问题
问题:基于指标的扩容存在滞后:
流量突增 → CPU 超阈值 → 触发扩容 → 启动新实例(30s-2min)
→ 在这段时间内,系统已经过载
解决方案:
1. 降低触发阈值(如 CPU 60% 而非 80%,提前触发)
2. 预热策略(新实例启动后逐步接入流量,避免冷启动影响)
3. 预测性扩容(提前扩容,而非等到过载)
K8s HPA/KEDA 弹性实战
HPA(Horizontal Pod Autoscaler)
HPA 是 K8s 原生的水平自动扩缩容能力:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3 # 最小实例数(保证基础可用性)
maxReplicas: 20 # 最大实例数(控制成本)
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # CPU 使用率超过 60% 触发扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70 # 内存使用率超过 70% 触发扩容
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # 扩容冷却:60s 内不重复触发
policies:
- type: Percent
value: 100 # 每次最多扩容 100%(翻倍)
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 缩容冷却:5 分钟,避免频繁缩容
policies:
- type: Percent
value: 10 # 每次最多缩容 10%(保守)
periodSeconds: 60
HPA 的局限:只支持 CPU/内存指标,无法基于自定义业务指标(如 QPS、MQ 积压量)触发。
KEDA(Kubernetes Event-Driven Autoscaling)
KEDA 扩展了 HPA,支持基于任意外部事件/指标触发弹性伸缩:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-consumer-scaler
spec:
scaleTargetRef:
name: order-consumer
minReplicaCount: 1
maxReplicaCount: 50
triggers:
# 基于 Kafka 消息积压量触发
- type: kafka
metadata:
bootstrapServers: kafka:9092
consumerGroup: order-consumer-group
topic: order-topic
lagThreshold: "1000" # 积压超过 1000 条时触发扩容
# 基于 Prometheus 自定义指标触发
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: order_qps
query: sum(rate(http_requests_total{endpoint="/order/create"}[1m]))
threshold: "500" # QPS 超过 500 时触发扩容
扩缩容注意事项
| 注意点 | 说明 |
|---|---|
| 预热时间 | 新实例启动后需要 JVM 预热(JIT 编译),建议配置 readinessProbe 延迟接入 |
| 优雅关闭 | 缩容时给实例足够时间处理完在途请求,配置 terminationGracePeriodSeconds |
| 资源 Request/Limit | HPA 基于 Request 计算使用率,Request 设置过低会导致 HPA 过早触发 |
| 最小实例数 | 不要设为 0(除非是 KEDA 的 scale-to-zero 场景),避免冷启动延迟 |
大促容量保障流程
来自《容量保障核心技术与实战》(吴骏龙)的实战经验:
时间线
flowchart LR
A["T-30天<br/>容量评估"] --> B["T-14天<br/>压测演练"] --> C["T-7天<br/>扩容到位"] --> D["T-1天<br/>最终检查"] --> E["活动当天<br/>实时监控"] --> F["T+1天<br/>复盘总结"]
T-30 天:容量评估
- 评估今年大促峰值 TPS(基于去年数据 × 增长预期)
- 识别所有核心链路的容量瓶颈
- 制定容量提升方案(扩容/优化/降级预案)
- 建立容量问题分级规范(P0/P1/P2,对应不同解决时限)
T-14 天:压测演练
- 全链路压测(模拟大促峰值流量)
- 验证容量提升方案的效果
- 发现并修复 P0/P1 容量问题
- 验证降级预案的可行性
压测执行原则:
分阶段施压:
10% 峰值 → 验证监控和告警
50% 峰值 → 验证基本容量
100% 峰值 → 验证是否达到目标
120% 峰值 → 验证超预期场景的处理
每个阶段稳定运行 10-15 分钟后再加压
出现问题立即停止,先恢复再分析
T-7 天:扩容到位
- 按容量评估结果完成扩容
- 验证新实例已正常接入流量
- 检查所有依赖服务(数据库/缓存/MQ)的容量是否充足
T-1 天:最终检查
- [ ] 所有 P0/P1 容量问题已解决
- [ ] 降级开关已就位,可以一键触发
- [ ] 监控告警已配置,值班人员已就位
- [ ] 限流阈值已设置(通常设为容量的 80%)
- [ ] 回滚方案已准备好
活动当天:实时监控
监控看板必看指标:
- 核心链路 TPS(与预期对比)
- 核心接口 P99 RT(超过 SLO 立即告警)
- 错误率(超过 0.1% 立即响应)
- 资源使用率(接近红线水位立即处置)
- MQ 消息积压量(超过阈值说明消费跟不上)
应急处置流程:
发现问题
↓ 30 秒内:确认问题现象,通知相关人员
↓ 1 分钟内:判断影响范围,决定是否触发降级
↓ 5 分钟内:执行应急方案(扩容/限流/降级)
↓ 15 分钟内:确认问题缓解
↓ 活动结束后:详细复盘
T+1 天:复盘总结
容量问题分级规范(驱动改进):
| 等级 | 定义 | 解决时限 |
|---|---|---|
| P0 | 导致核心业务不可用 | 24 小时内 |
| P1 | 影响核心业务性能,但未完全不可用 | 72 小时内 |
| P2 | 影响非核心功能 | 1 周内 |
| P3 | 潜在风险,暂无直接影响 | 1 个月内 |
参考资料
- 《容量保障核心技术与实战》— 吴骏龙(极客时间,ID:925934538)
- 《高楼的性能工程实战课》— 高楼(极客时间,ID:925760308)
- K8s HPA 官方文档
- KEDA 官方文档
- 《云原生架构白皮书》— 阿里云
评论 (0)