什么是缓存预热

张开发
2026/4/16 12:36:11 15 分钟阅读

分享文章

什么是缓存预热
缓存预热概念、原理与最佳实践摘要缓存预热是提升系统启动后性能的关键手段。本文详细讲解缓存预热是什么、为什么需要、如何实现并给出Spring Boot环境下的完整代码示例帮助你彻底解决缓存穿透和缓存击穿问题。1. 什么是缓存预热缓存预热是指在系统启动或缓存大规模失效之后主动将热点数据或全部必要数据提前加载到缓存如Redis中的过程。简单说让缓存“先热起来”而不是等用户请求来了才被动加载。系统启动执行缓存预热从数据库/源系统加载热点数据写入Redis缓存已预热用户请求直达缓存2. 为什么需要缓存预热没有缓存预热时系统刚启动或缓存大规模失效后缓存是空的。此时大量并发请求会直接打到数据库 →缓存穿透查不到的数据每次都查DB热点key突然失效 →缓存击穿大量请求同时查DB整体DB压力飙升甚至导致数据库崩溃缓存预热可以问题预热的作用缓存穿透预热后缓存中存在数据不会穿透到DB缓存击穿热点key已经存在于缓存中不会突然失效启动时雪崩提前加载数据避免启动瞬间DB被打垮用户体验首次访问即可命中缓存响应更快3. 常见应用场景电商系统商品详情、分类信息、库存信息预热内容平台热门文章、推荐列表预热配置中心系统配置、开关配置预热用户会话用户权限、基本信息预热非全部用户只预热活跃用户4. 缓存预热的实现方式4.1 三种预热触发时机触发时机说明适用场景系统启动时应用启动完成后立即预热数据量不大或核心数据必须存在定时任务定期刷新热点数据如每小时数据变化较频繁需要保持缓存新鲜度手动触发通过接口或运维命令触发预热数据变更后需要手动更新缓存4.2 预热流程图系统启动时Redis数据库应用服务Redis数据库应用服务loop[逐条或批量写入]后续用户请求直接从Redis获取Spring容器启动完成1. 查询需要预热的数据(如所有商品ID列表)返回数据2. SET key value EX ttl3. 预热完成正常对外服务5. 代码实战Spring Boot 实现缓存预热5.1 方式一实现InitializingBeanComponentpublicclassCachePreheatimplementsInitializingBean{AutowiredprivateRedisTemplateString,ObjectredisTemplate;AutowiredprivateProductServiceproductService;// 数据库查询服务OverridepublicvoidafterPropertiesSet()throwsException{// 预热逻辑preheat();}privatevoidpreheat(){// 查询需要预热的数据如所有上架商品IDListLongproductIdsproductService.getAllOnShelfProductIds();for(Longid:productIds){ProductproductproductService.getById(id);Stringkeyproduct:id;// 写入缓存过期时间24小时redisTemplate.opsForValue().set(key,product,24,TimeUnit.HOURS);}System.out.println(缓存预热完成共预热 productIds.size() 条数据);}}5.2 方式二实现CommandLineRunnerComponentSlf4jpublicclassCachePreheatRunnerimplementsCommandLineRunner{AutowiredprivateRedisTemplateString,ObjectredisTemplate;AutowiredprivateHotDataServicehotDataService;Overridepublicvoidrun(String...args)throwsException{log.info(开始执行缓存预热...);ListHotDatahotListhotDataService.getTop1000HotData();// 只预热前1000热点for(HotDatadata:hotList){redisTemplate.opsForValue().set(hot:data.getId(),data,1,TimeUnit.HOURS);}log.info(缓存预热结束共 {} 条,hotList.size());}}5.3 方式三使用PostConstruct更简单ComponentpublicclassCachePreheatPostConstruct{AutowiredprivateRedisTemplateString,ObjectredisTemplate;PostConstructpublicvoidinit(){// 预热代码}}注意PostConstruct在Bean初始化后、尚未完全对外提供服务前执行适合简单预热。6. 缓存预热的进阶策略6.1 全量预热 vs 增量预热策略做法优点缺点全量预热将所有数据加载到缓存命中率最高内存占用大启动慢热点预热只预热访问频率最高的N条数据内存效率高启动快需要预先统计热点推荐先统计访问日志或使用LFU算法确定热点仅预热热点数据。6.2 定时预热 增量刷新Scheduled(cron0 0 2 * * ?)// 每天凌晨2点执行publicvoidscheduledPreheat(){// 重新加载热点数据覆盖旧缓存preheat();}6.3 预热时批量写入优化避免循环单条set使用Pipeline或批量操作redisTemplate.executePipelined((RedisCallbackObject)connection-{for(Productp:products){byte[]key(product:p.getId()).getBytes();byte[]valueJSON.toJSONString(p).getBytes();connection.setEx(key,86400,value);}returnnull;});7. 注意事项与最佳实践7.1 避免重复预热使用标志位如Redis中的preheat:done防止多次预热。预热前清空原有缓存视情况决定建议先清再写或增量合并。7.2 预热数据量控制如果数据量极大千万级预热会导致启动缓慢、内存飙升。解决方案分页查询分批写入使用SCAN或异步处理。7.3 预热失败处理预热过程中发生异常不应阻塞应用启动。可以捕获异常记录日志稍后重试或依赖懒加载兜底。7.4 与缓存淘汰策略的配合预热写入时设置合理的TTL避免永久缓存占用内存。如果使用allkeys-lru预热的热点数据会被LRU保护冷数据自动淘汰。7.5 不要预热所有DB数据只预热真正会被高频访问的数据。否则浪费内存且可能导致Redis内存淘汰掉真正的热点。8. 总结核心点说明定义系统启动或缓存失效后主动将热点数据加载到缓存作用避免缓存穿透/击穿保护数据库提升用户体验实现方式InitializingBean、CommandLineRunner、PostConstruct进阶策略热点预热、定时刷新、批量写入、异步预热注意事项控制数据量、处理异常、避免重复预热一句话总结缓存预热 主动填充 热点优先 合理TTL让缓存真正成为系统的“加速器”。

更多文章