第 3 章 · 可扩展性:水平扩展与缓存
本章讲解 如何应对流量增长:垂直/水平扩展、自动扩缩、缓存层次、异步削峰、限流降级与容量验证。
前置:PaaS Redis、Kafka;运维第 4 章滚动升级;架构第 1 章 NFR。
3.1 扩展性类型
| 类型 | 做法 | 上限 | 成本曲线 | 何时用 |
|---|
| 垂直扩展(Scale Up) | 加大 CPU/内存 | 单机硬件天花板 | 线性上升后陡增 | 有状态单点、快速救火 |
| 水平扩展(Scale Out) | 加 Pod 副本 | 需无状态 + LB | 近线性(理想) | 企业级首选 |
| 数据扩展 | 分库分表、读写分离 | 跨片查询复杂 | 研发运维双高 | 单库 > 500GB 或写 TPS 瓶颈 |
| 功能扩展 | 异步、缓存、CDN | 业务语义限制 | 架构复杂度 | 读多写少、峰值明显 |
量化信号该水平扩展了:
| 信号 | 阈值(参考) | 动作 |
|---|
| CPU sustained | > 70% 超 15min | HPA 扩容或升 limits |
| P99 延迟 | 较基线翻倍 | 查慢查询 / 加缓存 |
| 错误率 | > 0.1% 5xx | 限流 + 扩容 |
| 连接池等待 | queue > 0 持续 | 扩应用或 DB 连接池调优 |
3.2 Kubernetes 自动扩缩
3.2.1 HPA(Pod 级)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-api-hpa
namespace: app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 120
| 要点 | 说明 |
|---|
| 前置 | 集群安装 metrics-server;容器设 requests |
| minReplicas | 不低于 HA 要求(通常 ≥3) |
| scaleDown 窗口 | 防止流量抖动频繁缩容 |
| 自定义指标 | 可用 Prometheus Adapter 按 QPS 扩 |
3.2.2 VPA / Cluster Autoscaler(了解)
| 组件 | 作用 | 小紫场景 |
|---|
| VPA | 自动调 requests/limits | 谨慎:与 HPA 同用需分工 |
| CA | 节点不足时加 Worker | 裸金属集群需预置机器池 |
3.3 无状态扩展 checklist
| # | 项 | 验证 |
|---|
| 1 | Session 在 Redis | 扩缩容不丢登录 |
| 2 | 无本地写盘 | emptyDir 仅临时 |
| 3 | 幂等接口 | 重试不产生重复订单 |
| 4 | 优雅下线 | preStop + readiness 先摘流 |
| 5 | 连接池大小 | 副本数 × 池大小 < DB max_connections |
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
3.4 缓存层次
浏览器缓存 / CDN
↓
网关缓存(APISIX proxy-cache)
↓
应用本地缓存(Caffeine / dict 内存,秒级)
↓
分布式缓存 Redis(热点、Session,分钟~小时)
↓
数据库 + 读副本
| 层级 | 适用数据 | TTL 建议 | 一致性 |
|---|
| CDN | 静态 JS/CSS/图片 | 1h~7d | 最终一致 |
| 网关 | 公开商品列表 GET | 30s~5min | 可接受滞后 |
| 本地 | 配置、字典、类目树 | 60s + 主动失效 | 弱一致 |
| Redis | 库存快照、购物车、Session | 5min~24h | 业务可接受窗口 |
| DB | 权威数据源 | — | 强一致 |
3.4.1 缓存三大问题
| 问题 | 现象 | 对策 | 量化目标 |
|---|
| 穿透 | 查不存在 key,打穿 DB | 布隆过滤器;空值缓存 TTL 60s | 异常 key QPS 下降 90% |
| 击穿 | 热点 key 过期瞬间 DB 被打满 | 互斥锁;逻辑过期 | 热点 P99 不 spike |
| 雪崩 | 大量 key 同时过期 | 随机 TTL;多级缓存;熔断 | DB 连接不爆表 |
# 击穿防护 — 单飞(singleflight)伪代码
def get_product(pid):
key = f"product:{pid}"
val = redis.get(key)
if val:
return json.loads(val)
with redis.lock(f"lock:{key}", timeout=5):
val = redis.get(key) # double-check
if val:
return json.loads(val)
row = db.query_product(pid)
redis.setex(key, 300 + random.randint(0, 60), json.dumps(row))
return row
3.5 读写分离与分库(数据扩展入门)
| 模式 | 适用 | 复杂度 | 贤紫组件 |
|---|
| 读副本 | 读 >> 写(10:1+) | 中 | MySQL 主从 |
| 分库分表 | 单表亿级 | 高 | ShardingSphere / 应用路由 |
| CQRS 读模型 | 报表、搜索 | 高 | ES + 事件同步 |
行业案例 · 某内容平台:首页 Feed 直连 MySQL,日 PV 2000 万时 P99 1.2s。改造:Redis 热门 Feed + 异步刷新,读 QPS 从 8k 提到 45k,DB CPU 从 85% 降到 35%。