Skip to content

现在让你负责一个核心系统,需要实现核心数据的缓存预热,如何做? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

核心数据缓存预热,我先把目标说透:预热解决的是"冷启动问题"——服务重启或大促开始瞬间,缓存空着,海量请求直接砸穿到 DB,引发连锁雪崩。预热的价值 = 保住上线/大促前 10 分钟的交易不翻车。

预热什么:数据从哪来(业务分析是前提)

  • 静态热点:配置、字典、商品基础信息——启动时全量灌
  • 动态热点:Top 商品、活动页数据——来自历史访问分析(昨天/上周同时段的访问 Top 榜单)和运营指定(大促主推品清单)
  • 预热清单要数据驱动:我会让 BI 出"大促同时段缓存命中率贡献 Top 1000 的 key",按 80/20 圈预热范围——拍脑袋列清单必然漏

怎么做(三层机制)

  1. 启动预热:应用启动后、注册到注册中心之前,异步加载热点数据到本地缓存/Redis(可配置预热完成才放流量,配合权重慢慢调大)
  2. 定时刷新:低峰期定时任务刷新热点集合,防过期失效
  3. 大促专项:大促前全量预热 + 压测验证——预热是否生效要用压测验证(观察 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 打满影响线上交易的事故。