Redis缓存预热

一、前言:为什么需要 Redis 缓存预热?

想象这样一个场景:

  • 系统刚上线或重启
  • 所有缓存为空(冷启动)
  • 大量用户同时访问商品详情、用户信息等接口
  • 请求全部穿透到数据库 → DB 瞬间被打垮

这就是典型的"缓存雪崩"前置问题------缓存未就绪!

缓存预热(Cache Warm-up) 就是在系统启动前或低峰期,主动将热点数据加载到 Redis 中,避免冷启动带来的性能灾难。

本文将从策略设计、实现方案、避坑指南三个维度,带你掌握 Redis 缓存预热的完整实践。


二、什么数据值得预热?

不是所有数据都需要预热!聚焦高访问频次 + 低变更频率的数据:

数据类型 是否适合预热 示例
✅ 热点商品信息 双11主会场商品
✅ 用户基础资料 VIP 用户 profile
✅ 配置字典表 国家列表、状态码映射
❌ 用户订单列表 每人不同,且实时变化
❌ 实时库存 高频更新,预热即过期

💡 经验法则

  • 访问量 Top 1000 的 key
  • 更新频率 < 1 次/小时
  • 数据量可控(避免 OOM)

三、四大预热策略对比

策略 原理 优点 缺点 适用场景
启动时预热 应用启动时加载 简单直接 阻塞启动、单点压力大 小型系统
定时任务预热 定时从 DB 同步 不阻塞主流程 有延迟 中低频更新数据
双写+渐进预热 写 DB 时同步写缓存 实时性强 逻辑复杂 核心热点数据
离线计算+批量导入 大数据平台生成快照 海量数据支持 架构复杂 超大型系统

推荐组合启动预热(核心) + 定时任务(兜底)


四、实战 1:Spring Boot 启动时预热(最常用)

场景

  • 系统启动时加载 1000 个热门商品到 Redis

代码实现

java 复制代码
@Component
public class CacheWarmUpRunner implements CommandLineRunner {

    @Autowired
    private ProductMapper productMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void run(String... args) {
        log.info("开始缓存预热...");

        // 1. 查询热点商品(按访问量排序)
        List<Product> hotProducts = productMapper.selectHotProducts(1000);

        // 2. 批量写入 Redis(使用 Pipeline 提升性能)
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            hotProducts.forEach(product -> {
                String key = "product:" + product.getId();
                String value = JSON.toJSONString(product);
                connection.set(key.getBytes(), value.getBytes());
                connection.expire(key.getBytes(), Duration.ofHours(2).getSeconds());
            });
            return null;
        });

        log.info("缓存预热完成,共加载 {} 个商品", hotProducts.size());
    }
}

关键优化

  • 使用 executePipelined 减少网络往返
  • 设置合理 TTL(如 2 小时),避免脏数据长期滞留

五、实战 2:OpenResty + Lua 预热(网关层兜底)

即使后端做了预热,仍可能因扩容、故障导致部分实例缓存缺失。
在 OpenResty 层实现"懒加载 + 自动回源预热"

Lua 复制代码
location /api/product {
    content_by_lua_block {
        local id = ngx.var.arg_id
        local key = "product:" .. id

        -- 1. 先查 Redis
        local redis = require "resty.redis"
        local red = redis:new()
        red:connect("127.0.0.1", 6379)
        local cache = red:get(key)

        if cache and cache ~= ngx.null then
            ngx.say(cache)
            return
        end

        -- 2. 缓存未命中:回源查询 DB(通过内部 API)
        local res = ngx.location.capture("/internal/product?id=" .. id)
        
        if res.status == 200 then
            -- 3. 写入 Redis(设置较短 TTL,如 60s)
            red:set(key, res.body)
            red:expire(key, 60)
            ngx.print(res.body)
        else
            ngx.status = res.status
            ngx.print(res.body)
        end

        red:close()
    }
}

🔁 效果

  • 首次请求稍慢,但后续请求极速响应
  • 自动重建缓存,无需人工干预

六、高级技巧:分片预热 + 限流保护

问题

  • 一次性加载 100 万条数据 → Redis CPU 打满
  • 网络带宽耗尽

解决方案:分批次 + 限速

java 复制代码
// 分页预热
int pageSize = 1000;
int total = productMapper.countHotProducts();
int pages = (total + pageSize - 1) / pageSize;

for (int i = 0; i < pages; i++) {
    List<Product> batch = productMapper.selectHotProductsPage(i, pageSize);
    // 写入 Redis...
    
    // 限速:每批间隔 100ms
    Thread.sleep(100);
}

📊 建议指标

  • 单次批量 ≤ 5000 条
  • QPS 控制在 Redis 承载能力的 50% 以内

七、避坑指南:常见错误与解决方案

坑点 后果 解决方案
未设 TTL 脏数据永久滞留 所有预热 key 必须设置 TTL
全量预热 Redis OOM 只预热 Top N 热点数据
同步阻塞启动 应用启动超时 改为异步线程 + 健康检查延迟就绪
忽略数据一致性 缓存与 DB 不一致 预热后监听 binlog 增量更新

💡 健康检查示例(K8s)

复制代码
readinessProbe:
  exec:
    command: ["sh", "-c", "redis-cli EXISTS product:1"]
  initialDelaySeconds: 30  # 等待预热完成

八、监控与验证

如何确认预热成功?

  1. Redis 监控

    bash 复制代码
    redis-cli INFO memory  # 查看 used_memory
    redis-cli DBSIZE       # 查看 key 数量
  2. 应用日志:记录预热数量与耗时

  3. 压测对比

    • 冷启动 QPS:500
    • 预热后 QPS:15,000+

九、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
希望永不加班2 小时前
SpringBoot 整合 MyBatis 完整实战
java·spring boot·后端·spring·mybatis
wuqingshun3141593 小时前
说说事务的隔离级别
java·spring
zs宝来了3 小时前
Redis 哨兵机制:Sentinel 原理与高可用实现
redis·sentinel·高可用·源码解析·哨兵
会飞的大可4 小时前
Redis 故障排查与应急手册:从理论到实践
数据库·redis·缓存
小红的布丁4 小时前
Redis 内存淘汰与过期策略
java·spring·mybatis
huihuihuanhuan.xin4 小时前
spring循环依赖以及补充相关知识
java·后端·spring
shark22222224 小时前
springboot中配置logback-spring.xml
spring boot·spring·logback
问简4 小时前
MISCONF Errors writing to the AOF file: Bad file descriptor
redis