什么是预热?它有什么作用?

#中间件#面试八股

什么是预热?它有什么作用?

结论:预热(Warm-up)是在真实流量到来之前,把系统从「冷」拉到「热」:缓存加载热点数据、连接池提前建连、JIT 编译热点方法、限流器从低阈值爬升、CDN 回源分发。不做预热,冷启动瞬间的缓存穿透、建连与类加载会把 P99 拉高数倍,甚至引发雪崩。

一、预热的六个层次

层次冷的后果预热手段
JVM解释执行、类加载慢启动后回放流量、调 CompileThreshold、原生镜像
连接池首个请求建连超时启动时按 minimumIdle 建连并 SELECT 1 探活
本地缓存命中率为 0,全部打 DB启动加载热点 key、refreshAfterWrite
分布式缓存缓存为空,穿透到 DB大促前批量灌数据、pipeline 导入
限流器冷启动即满速,压垮下游预热模式,阈值从 1/3 线性爬升
CDN回源风暴提前刷新与预推

二、冷启动曲线

 QPS
  │                        ┌────────────── 稳态
  │                   ╱────┘
  │             ╱────┘    ← 预热爬升:JIT + 缓存 + 连接
  │       ╱────┘
  │ ─────┘
  │ ▲ 冷启动瞬间:RT 高、错误率高、DB 压力尖峰
  └──────────────────────────────────────────► t

限流器的预热同理:Guava 的 SmoothWarmingUp 会在 warmupPeriod 内把放行速率从 stableInterval / coldFactor 线性提升到目标速率。

三、缓存预热代码

@Component
public class HotDataWarmer implements ApplicationRunner {
    private final RedisTemplate<String, String> redis;
    private final SkuMapper skuMapper;

    @Override
    public void run(ApplicationArguments args) {
        List<Long> hotSkuIds = skuMapper.selectTopHotSkuIds(5_000);
        redis.executePipelined((RedisCallback<Object>) conn -> {
            for (Long id : hotSkuIds) {
                String json = loadSkuJson(id);
                if (json == null) {
                    continue;
                }
                // TTL 加随机抖动,避免预热数据在同一刻集体过期
                long ttl = 3600 + ThreadLocalRandom.current().nextInt(600);
                conn.stringCommands().setEx(
                        ("sku:" + id).getBytes(StandardCharsets.UTF_8),
                        ttl,
                        json.getBytes(StandardCharsets.UTF_8));
            }
            return null;
        });
        log.info("热点 SKU 预热完成,共 {} 条", hotSkuIds.size());
    }

    private String loadSkuJson(Long id) {
        Sku sku = skuMapper.selectById(id);
        return sku == null ? null : JsonUtil.toJson(sku);
    }
}

用 pipeline 把 5000 次 SET 合并成一次往返;TTL 抖动是防止预热完就在同一秒集体失效。

四、限流预热配置

# Sentinel 冷启动预热:阈值在 warmUpPeriodSec 内从 1/coldFactor 爬升到 count
flow:
  - resource: orderCreate
    grade: 1                # QPS 模式
    count: 500
    controlBehavior: 2      # WARM_UP
    warmUpPeriodSec: 60
    maxQueueingTimeMs: 0

五、常见追问

Q1:预热数据量太大怎么办? 只预热 Top N(按访问日志排序),其余靠懒加载加互斥重建;同时给缓存写空值占位,避免穿透。

Q2:怎么验证预热生效? 看三个指标:缓存命中率是否在启动后立刻达到稳态、P99 是否还有冷启动尖峰、DB QPS 是否被削平。

Q3:预热和缓存雪崩什么关系? 预热是提前填满,雪崩是同时失效。两者必须配合:预热时给 TTL 加随机抖动,否则预热完仍会在同一时刻集体过期。

Q4:预热的代价是什么? 启动变慢、启动瞬间对 DB 有一波集中读。要给预热加限速与开关,避免应用重启风暴把数据库打挂。

小结

  • 预热覆盖 JVM、连接池、缓存、限流器、CDN 六个层次,缺一层就有一处尖峰。
  • 缓存预热要配 pipeline 与 TTL 抖动,否则预热本身会成为新尖峰。
  • 用命中率、P99、DB QPS 三个指标验证预热是否真的生效。
笔记加载中…