服务器启动缓存预热技术

缓存预热(Cache Warming),本质上是:

在流量真正到达之前,把未来会被访问的数据提前放进缓存,从而避免冷启动时大量请求直接打到数据库或下游服务。


1.为什么需要缓存预热

1
2
3
4
5
6
7
8
9
10
11
12
用户请求
↓
Cache
├── HIT → 直接返回
│
└── MISS
↓
DB / API
↓
写入 Cache
↓
返回

缓存失效:

  • 服务重启
  • Redis 故障恢复
  • 集群扩容
  • 缓存全部过期
  • 发布新版本
  • 大促开始

那么瞬间会出现大量:

1
2
3
4
5
6
7
8
9
10
11
12
13
Cache MISS
↓
大量请求同时查询DB
↓
DB连接池耗尽
↓
DB响应变慢
↓
请求超时
↓
重试
↓
流量进一步放大

典型的 缓存击穿/冷启动雪崩。

预热的目的:

1
2
3
4
5
请求来了 → 才加载

变成:

请求来之前 → 已经加载

2.缓存预热的几种方式

2.1启动时预热

服务启动后主动加载热点数据。

1
2
3
4
5
6
7
8
9
Application Start
↓
读取热点Key列表
↓
DB/ MQ/ 文件
↓
写入Redis
↓
Ready

适合:

  • 配置
  • 字典
  • 商品分类
  • 权限
  • 热门商品
  • 基础数据

元数据预热


2.2定时预热

按照固定周期更新热点数据:

1
2
3
4
5
6
7
   Scheduler
↓
获取 Top N热点
↓
查询数据
↓
更新Cache

热点变化以后,你可能要等到下一次周期才能发现。因此通常会进一步演进成事件驱动。


2.3事件驱动预热

这是比较成熟的方式如商品库存、价格、内容发生变化:

1
2
3
4
5
6
7
8
9
DB
↓
CDC / Binlog
↓
Kafka
↓
Cache Consumer
↓
Redis

或者:

1
2
3
4
5
6
7
8
9
商品更新
↓
发布 ProductUpdated Event
↓
消费者收到事件
↓
查询最新商品
↓
更新 Cache

这样缓存不是:

1
2
3
4
定期刷新

而是:
数据变化 → 缓存同步

典型场景:

  • 商品
  • 用户
  • 配置
  • 订单状态
  • 库存
  • 内容
  • 权限

2.4基于热点预测的预热

真正有价值的缓存预热,不是:

“把所有数据都放进去。”

而是:

只预热未来最可能访问的数据。

3. Redis预热时最容易犯的错误

3.1一次性全部加载

例如:

1
2
3
4
5
1000 万条数据
↓
一次性查询
↓
一次性写 Redis

结果可能是:

1
2
3
4
DB 内存压力
Redis 网络压力
应用 JVM OOM
Redis CPU 飙升

正确方式:

1
2
3
4
5
6
7
分页
↓
Batch
↓
限速
↓
异步

控制预热速率。


3.2所有实例一起预热

例如 Kubernetes:

1
Pod × 100

每个 Pod 启动:

1
load 100 万数据

实际上:

1
100 × 100万

请求全部打向 DB应该把预热从:

1
2
3
4
5
6
7
Application

剥离成:
Warm-up Worker

或者使用:
Leader Election

只让一个节点负责。


4. 预热/缓存击穿要结合起来

即使做了预热,也无法保证 100% 命中。因此必须设计:

4.1Cache Miss防护

1
2
3
4
5
6
7
8
9
10
11
12
13
Request
↓
Redis
↓
MISS
↓
Distributed Lock
↓
只有一个线程查询DB
↓
写Redis
↓
其他线程读取结果

即:

1
2
3
           ┌→ DB → Redis
MISS → Lock┤
└→ Wait → Redis

这就是:
Single Flight / Mutex / Distributed Lock


4.2预热 + TTL 是一个关键矛盾

通常为了避免可能出现大量 Key 同时过期。因此通常加入随机TTL

随机 TTL

1
TTL = 30min + random(0~10min)

5. 生产级架构

1
2
3
4
5
6
7
8
9
10
11
12
13
   ┌──────────────┐
│ 用户请求流量 │
└──────┬───────┘
↓
Cache
↙ ↘
HIT MISS
↓ ↓
Return SingleFlight
↓
DB
↓
Cache

需关注的指标

指标 意义
Cache Hit Rate 缓存命中率
Warm-up Hit Rate 预热数据实际命中率
Cache Miss QPS 未命中压力
DB QPS 缓存对DB的保护效果
Warm-up Duration 预热耗时
Warm-up Failure Rate 预热失败率
Redis CPU Redis 压力
Redis Memory 内存占用
Hot Key Count 热点 Key 数
Eviction Rate 淘汰速度
Stale Read Rate 过期/旧数据读取情况

其中一个非常重要的指标是:

预热命中贡献率

因为:预热了 100 万个 Key并不意味着预热有效。如果真正访问的只有:1000 个那就是资源浪费。


从本质上理解

缓存预热可以抽象成一个资源配置问题:

1
2
3
4
5
6
7
有限 Cache 容量
↓
有限预热成本
↓
预测未来访问概率
↓
选择最值得缓存的数据

所以真正的目标不是:让缓存尽可能满。 而是:让有限的缓存空间覆盖尽可能多的未来请求。