现在让你负责一个核心系统,需要实现核心数据的缓存预热,如何做?
- 标签:后端、场景题
🏛 业务架构师
核心数据缓存预热,我先把目标说透:预热解决的是"冷启动问题"——服务重启或大促开始瞬间,缓存空着,海量请求直接砸穿到 DB,引发连锁雪崩。预热的价值 = 保住上线/大促前 10 分钟的交易不翻车。
预热什么:数据从哪来(业务分析是前提)
- 静态热点:配置、字典、商品基础信息——启动时全量灌
- 动态热点:Top 商品、活动页数据——来自历史访问分析(昨天/上周同时段的访问 Top 榜单)和运营指定(大促主推品清单)
- 预热清单要数据驱动:我会让 BI 出"大促同时段缓存命中率贡献 Top 1000 的 key",按 80/20 圈预热范围——拍脑袋列清单必然漏
怎么做(三层机制)
- 启动预热:应用启动后、注册到注册中心之前,异步加载热点数据到本地缓存/Redis(可配置预热完成才放流量,配合权重慢慢调大)
- 定时刷新:低峰期定时任务刷新热点集合,防过期失效
- 大促专项:大促前全量预热 + 压测验证——预热是否生效要用压测验证(观察 DB 回源 QPS 接近零),而不是看任务跑完了
必须设计的保护机制(架构师重点)
- 预热失败要熔断:预热任务拖垮 DB 就本末倒置——限速加载(如 QPS 2000)、失败跳过并告警,宁可预热不完整,不能把库预热崩
- 预热与真实流量隔离:预热请求打标记,避免统计污染和预热挤占用户请求
- 缓存永不过期 + 逻辑过期:核心热点 key 物理不过期,业务字段带时间戳异步刷新,杜绝"到期瞬间击穿"
收尾:预热是典型的花小钱防大事故——它不能提升峰值性能,但决定了重启、扩容、大促开闸这几个"最脆弱时刻"的存活。预案必须演练,没演练过的预热脚本等于没有。
🏗 系统架构师
缓存预热的难点不是"把数据塞进 Redis",而是预热速度不拖垮数据源、预热完成度可度量、预热不引发一致性问题。我按流程讲。
1. 预热什么:圈定热点集合
- 全量还是热点?按 28 法则圈 TopN:核心商品、活动页、类目树、配置数据。数据源是数仓统计的访问 Top 榜 + 业务清单(运营录入的大促商品)+ 历史趋势
- 明确每个 key 的 TTL 与更新策略:静态配置长 TTL + 变更主动刷新;业务数据短 TTL + 兜底回源
2. 怎么预热:控制速率的分批加载
- 预热任务读数据源(DB 批量查 / 数仓文件),按固定速率(令牌桶限速)写 Redis——必须限速,否则预热瞬间把 DB 打挂,核心系统自己制造事故
- 多节点执行要分片:按 key hash 切片分发(或用任务调度平台的分片广播),避免重复加载;加分布式锁保证任务不并发
- 大 value(如整页缓存)预热要异步构建,避免单线程卡死
3. 时序与触发
- 发布/扩容流程里把"预热完成"作为流量就绪的 gate:K8s 的 readinessProbe 检查预热标记,或发布系统等预热任务 100% 才放流量
- 定时增量预热:每日低峰刷新当天热点;大促前 T-1 全量预热
4. 度量与兜底
- 预热完成度指标:目标 key 数 / 已加载数,缺口告警
- 上线后监控缓存命中率(首小时命中率应 > 90%),未达标自动触发二次预热
- 一致性:预热期间源数据变更怎么办——预热写缓存用"加载时间戳 + 源表 updated_at 比对",变更过的跳过或重读
一句话:圈热点、限速率、分片跑、卡门禁、量命中率,五个环节做全才叫核心系统的预热。
💻 全栈开发专家
核心数据预热我做过的方案:启动预热 + 定时增量刷新 + 灰度放量,关键是别让冷启动打死 DB。
1. 识别"核心数据"
先圈范围:哪些 key 是请求路径必经的(配置、商品主表、白名单、字典),预热列表显式维护(配置中心可改),别拍脑袋全量灌。
2. 启动预热(ApplicationRunner)
java
@Component
public class CacheWarmUp implements ApplicationRunner {
public void run(ApplicationArguments args) {
List<String> keys = configCenter.getWarmUpKeys(); // 分批加载列表
keys.parallelStream().forEach(k -> {
Object v = db.load(k);
redis.setex(k, ttl + random(300), v); // 随机 TTL 防雪崩
});
}
}要点:
- 并行但限流:自定义并行流底层是公共池,我用独立线程池控制并发(比如 8),防止预热把 DB/Redis 打挂。
- 分批 + 进度上报:大 key(如 10w 条的字典)分片加载,进度打点,K8s 里把就绪探针绑到预热完成标记上,没预热完不放流量。
3. 定时增量刷新(防过期/防漂移)
XXL-JOB 每 5 分钟拉变更数据(update_time > last)刷新缓存,配合消息订阅(数据变更事件)做准实时失效。
4. 灰度与兜底
- K8s 滚动更新时新 Pod 先预热再 ready,天然避免冷启动流量;
- 预热失败的 key 不阻塞启动,记录清单靠定时任务补偿;
- 布隆过滤器、空值缓存这些防击穿手段同步上。
坑:预热数据要来自从库或专门只读实例,我见过预热流量把主库 CPU 打满影响线上交易的事故。