跳至主要內容

容量规划与架构容量模型:从QPS到资源预估

郑天祺大约 8 分钟架构设计容量规划架构容量QPS资源预估

前言

架构师需要回答的一个关键问题:

"这个系统最多能支持多少用户?"
"618大促来了,现有基础设施够吗?"
"扩容需要加多少台服务器?"

这些问题的答案不是拍脑门,而是基于容量规划模型的科学计算。

本文从业务指标出发,逐步推导出硬件资源需求,建立完整的架构容量模型。


一、容量规划的核心指标

1.1 流量指标层级

DAU (Daily Active Users) ← 日活跃用户数
  └─ 每天有多少用户使用系统
  └─ 关键指标,用于长期规划

PCU (Peak Concurrent Users) ← 峰值并发用户数
  └─ 某个时刻最多有多少用户在线
  └─ 关键指标,用于短期容量规划

QPS (Queries Per Second) ← 每秒查询数
  └─ 最关键的技术指标
  └─ 直接决定硬件规模

TPS (Transactions Per Second) ← 每秒事务数
  └─ QPS的子集(某些请求才算一个事务)

Throughput ← 吞吐量
  └─ 单位时间处理的数据量(MB/s)

1.2 从DAU推导QPS

假设:DAU = 100万

计算步骤:

1. 计算PCU(峰值并发用户)
   假设:日均活跃时间分布
   ├─ 08:00-12:00: 40% 活跃
   ├─ 12:00-18:00: 60% 活跃(午间高峰)
   ├─ 18:00-23:00: 30% 活跃
   └─ 其余时间: 5% 活跃
   
   峰值时刻(18:00):
     PCU = 100万 * 60% / (活跃时长 / 24小时)
        = 100万 * 60% / 6小时
        = 100万 * 0.1
        = 10万 PCU

2. 计算平均QPS
   假设:每个用户平均每秒发起 0.1 个请求
   平均QPS = PCU * 0.1 = 10万 * 0.1 = 1万 QPS

3. 计算峰值QPS(突发流量)
   峰值因子 = 1.5 - 2.0(用户行为聚合导致突发)
   峰值QPS = 平均QPS * 峰值因子 
          = 1万 * 1.5 = 1.5万 QPS

4. 考虑多个业务线的QPS
   总QPS = 订单QPS + 支付QPS + 库存QPS + ...
        = 5000 + 3000 + 2000 + ...
        = 15000 QPS

1.3 QPS to Latency

系统性能指标:

QPS: 1万 QPS
平均延迟:50ms
P99延迟:200ms

计算:
  一个请求需要 50ms 才能完成
  同时最多能处理 1000 / 50 * 1000 = 20 个并发请求
  
  但如果QPS=10000,需要多少并发连接?
  并发连接数 = QPS * 平均延迟(秒)
             = 10000 * 0.05
             = 500 个并发连接

二、单机容量模型

2.1 CPU瓶颈分析

单核CPU可以处理的QPS = 10000 (取决于业务逻辑复杂度)

常见应用的单核QPS:
├─ 静态文件服务(Nginx): 50000 QPS
├─ 简单API(Hello World): 10000 QPS
├─ 数据库查询(有缓存): 5000 QPS
├─ 复杂业务逻辑: 1000 QPS
└─ 机器学习推荐: 100 QPS

现代CPU配置:8核

单机QPS = 单核QPS * CPU核数 * 利用率
        = 10000 * 8 * 0.7
        = 56000 QPS

(不能100%利用,通常70%左右,留余量应对突发)

2.2 内存瓶颈分析

Java应用堆内存计算:

基础JVM: 512MB
├─ 启动时自动分配

业务对象: 100MB
├─ 例如:缓存1000个用户对象
├─ 每个对象100KB
└─ 1000 * 100KB = 100MB

连接池: 200MB
├─ 100个数据库连接
├─ 每个连接2MB (协议缓冲、事务日志等)
└─ 100 * 2MB = 200MB

框架库: 300MB
├─ Spring、Hibernate等

总计: 512 + 100 + 200 + 300 = 1112MB ≈ 2GB

推荐:
├─ 小型应用:2GB
├─ 中型应用:4-8GB
├─ 大型应用:16GB+

内存与QPS的关系:
  增加内存 → 缓存更多数据 → 减少数据库查询 → QPS提升
  
  例如:
  2GB内存:缓存1000个对象,hit=70% → QPS=5000
  8GB内存:缓存10000个对象,hit=90% → QPS=8000

2.3 网络IO瓶颈

网络带宽 = 1Gbps (1000Mbps)

应用场景分析:

场景1:API服务(JSON响应)
  平均响应大小:10KB
  最大吞吐:1000Mbps / (10KB * 8bits/byte)
         = 1000Mbps / 80Kbps
         = 12500 requests/sec = 12500 QPS

场景2:大文件下载(视频/图片)
  平均文件大小:5MB
  最大吞吐:1000Mbps / (5MB * 8)
         = 1000Mbps / 40Mbps
         = 25 requests/sec = 25 QPS(网络极限)

网络优化方案:
  ├─ CDN分发:减少跨域流量
  ├─ 压缩:gzip压缩减少60%数据量
  ├─ 升级带宽:10Gbps网卡
  └─ 多线路:多ISP链接负载均衡

2.4 磁盘IO瓶颈

磁盘类型与IOPS(每秒操作次数):

SSD: 100000 IOPS
HDD: 1000 IOPS

数据库写入分析:

假设:每个写请求需要 3次磁盘操作
  ├─ 写入binlog(MySQL主从同步)
  ├─ 写入redo log(事务日志)
  └─ 写入数据文件

磁盘限制的QPS:
  SSD: 100000 / 3 ≈ 33000 write-QPS
  HDD: 1000 / 3 ≈ 333 write-QPS

优化方案:
  ├─ 使用SSD: HDD → SSD, 提升100倍
  ├─ WAL优化: 批量写入 + 异步fsync
  ├─ 主从分离: 写向master, 读向slave
  └─ 分库分表: 多个数据库并行处理

2.5 单机容量总结

假设应用:
  ├─ 平均QPS: 1000
  ├─ 峰值QPS: 2000
  ├─ 平均延迟: 50ms
  ├─ 需要持久化(数据库)

计算单机配置:

CPU:
  需要QPS 2000 (峰值) / 10000 (单核能力) = 0.2个CPU
  考虑突发和降级,选择 4核 CPU
  
内存:
  业务缓存 + 连接池 + JVM开销
  选择 8GB
  
磁盘:
  写QPS 200 (估算) / 100000 (SSD能力) = 0.002
  磁盘不是瓶颈,使用500GB SSD即可
  
网络:
  QPS 2000 * 平均响应10KB = 20MB/s
  千兆网卡 (125MB/s) 充足

单机成本:
  ├─ 服务器:¥3000-5000
  ├─ 带宽:¥500/月
  └─ 数据库:¥5000/月(云数据库)

三、分布式容量模型

3.1 水平扩容

需求:QPS 100万

单机能力:
  ├─ QPS: 10000
  ├─ 成本: ¥10000/年

需要服务器数:
  100万 / 10000 = 100 台

关键问题:
  ├─ 如何分散流量? → 负载均衡
  ├─ 如何避免单点? → 冗余设计
  └─ 如何监控100台? → 自动化运维

架构:
┌──────────────┐
│ Load Balancer│
└──────────────┘
        │
    ┌───┴───┬────┬────┬─────┐
    ▼       ▼    ▼    ▼     ▼
  Srv1    Srv2  Srv3 ...  Srv100
  
  每个服务器 10000 QPS
  100个服务器 = 100万 QPS

3.2 数据库容量规划

问题:100台应用服务器,但只能有1个数据库吗?

数据库瓶颈分析:

写QPS = 100万 * 10% = 10万 write/sec
读QPS = 100万 * 90% = 90万 read/sec

单台MySQL能力:
  ├─ 写:10000 write/sec
  └─ 读:50000 read/sec

需要的数据库:
  写:10万 / 10000 = 10台 master
  读:90万 / 50000 = 18台 slave

但生产方案(主从复制):
  ├─ 1个Master(写)
  ├─ 3-5个Slave(读)
  └─ 可以分库分表来扩展

分库分表策略:
  ├─ 按用户ID分库:100个库,每库 100万用户/100 = 1万用户
  ├─ 每个库1个Master + 2个Slave
  └─ 总计:100个Master + 200个Slave = 300台数据库服务器

成本:太高!通常采用:
  ├─ 云数据库(RDS):自动分片和备份
  └─ 年成本:100万 * ¥100/月 = ¥1.2亿

3.3 缓存容量规划

使用缓存减轻数据库压力:

假设:缓存hit rate = 90%

写QPS:10万 → 无法减少(必须写DB)
读QPS:90万 → 90% * 90万 = 81万 来自缓存
      剩余:9万 来自DB

缓存需求:
  ├─ 缓存热数据(Top 20% 用户数据)
  ├─ 20% * 1000万用户 = 200万用户
  ├─ 每个用户对象 1KB
  └─ 总内存:200万 * 1KB = 2GB

Redis容量规划:
  ├─ 单台Redis:30GB内存,可处理100K QPS
  ├─ 需要:2GB / 30GB = 0.1台(只需1台)
  ├─ 考虑主从备份和高可用:3台 (主+从+sentinel)
  └─ 年成本:3 * ¥5000 = ¥15000

效果:
  ├─ 缓存前:DB 10万 write/sec + 90万 read/sec = 100万 QPS
  ├─ 缓存后:DB 10万 write/sec + 9万 read/sec = 19万 QPS
  ├─ 数据库需求从300台 → 20台(大幅节省)
  └─ ROI极高

四、容量规划检查清单

4.1 计算方程式

QPS计算:
  QPS = DAU * 日均活跃时长(小时) / 24 * 每用户请求数/秒 * 峰值因子

服务器数量:
  Server_Count = Peak_QPS / Single_Server_QPS * Safety_Factor
  
  例:Peak_QPS = 10万, Single_Server_QPS = 10000, Safety = 1.5
      Server_Count = 100000 / 10000 * 1.5 = 15台

CPU核数:
  Cores = Peak_QPS / QPS_Per_Core / CPU_Utilization
  
  例:Peak_QPS = 10000, QPS_Per_Core = 1000, Utilization = 70%
      Cores = 10000 / 1000 / 0.7 = 14核

内存大小:
  Memory = Base_JVM + Cache_Size + Connection_Pool + Libraries
  
  例:512MB + 1GB + 500MB + 500MB = 2.5GB → 选4GB或8GB

数据库:
  DB_Write_Capacity = Write_QPS / Per_DB_Write_QPS
  DB_Read_Capacity = Read_QPS / Per_DB_Read_QPS * (1 - Cache_Hit_Rate)

4.2 容量规划表

┌────────────┬──────────┬──────────┬──────────┬──────────┐
│ 业务规模   │ DAU     │ Peak QPS │ 服务器数 │ 年成本   │
├────────────┼──────────┼──────────┼──────────┼──────────┤
│ 小型应用   │ 10万    │ 1000    │ 2        │ 10万     │
│ 中型应用   │ 100万   │ 10000   │ 20       │ 100万    │
│ 大型应用   │ 1000万  │ 100000  │ 200      │ 1000万   │
│ 超大规模   │ 1亿     │ 1000000 │ 2000     │ 1亿      │
└────────────┴──────────┴──────────┴──────────┴──────────┘

(仅计算应用层,不含数据库、缓存等)

五、容量监控与告警

5.1 关键指标监控

class CapacityMonitoring:
    """容量监控"""
    
    def __init__(self):
        self.thresholds = {
            'cpu_utilization': 0.75,  # CPU >75% 告警
            'memory_utilization': 0.80,  # 内存 >80% 告警
            'disk_utilization': 0.85,  # 磁盘 >85% 告警
            'qps_growth': 0.1,  # QPS周环比增长 >10% 告警
        }
    
    async def check_capacity(self):
        """定期检查容量"""
        while True:
            metrics = {
                'cpu': self.get_cpu_utilization(),  # 0-1
                'memory': self.get_memory_utilization(),
                'disk': self.get_disk_utilization(),
                'current_qps': self.get_current_qps(),
                'peak_qps_week_ago': self.get_peak_qps_week_ago(),
            }
            
            # QPS周环比增长告警
            qps_growth = (metrics['current_qps'] - metrics['peak_qps_week_ago']) / metrics['peak_qps_week_ago']
            if qps_growth > self.thresholds['qps_growth']:
                await self.alert(f"QPS周环比增长 {qps_growth:.1%}, 需要评估是否扩容")
            
            # CPU/内存/磁盘利用率告警
            for metric_name, threshold in self.thresholds.items():
                if metric_name in metrics:
                    if metrics[metric_name] > threshold:
                        await self.alert(f"{metric_name} 已达 {metrics[metric_name]:.1%}")
            
            await asyncio.sleep(300)  # 5分钟检查一次

5.2 扩容决策树

当前QPS = 8000, 单机容量 = 10000

┌─────────────────────────┐
│ QPS增长到8000           │
└────────────┬────────────┘
             │
      CPU利用率 ?
    ┌─────────┴─────────┐
   <70%               >70%
    │                  │
   否                 是
    │                  │
 继续观察          立即规划扩容
    │                  │
 监控后续增速        评估:
    │                 ├─ 垂直扩容(升级硬件)
    │                 ├─ 水平扩容(加服务器)
    │                 └─ 优化(改代码、加缓存)
    │
 若周环比 >15%        选择方案:
    │                 ├─ 加缓存最快(2-3天)
    │  └─────────┐    ├─ 垂直扩容(1周)
    │            │    └─ 水平扩容(2周)
    └────────────┘
         │
      扩容执行

总结

容量规划是架构师的核心技能:

  1. 了解业务指标(DAU → QPS)
  2. 分析单机瓶颈(CPU/内存/网络/磁盘)
  3. 计算所需资源(服务器数、DB数、缓存大小)
  4. 建立监控告警(及时发现容量问题)
  5. 制定扩容方案(快速响应增长)

黄金法则

  • 容量规划总是低估增长(数据往往比预期快)
  • 留足余量(70% CPU利用率,不是100%)
  • 定期压测验证(模型 ≠ 现实)
上次编辑于:
贡献者: zhengtianqi