不能用@PostConstruct做Redis预热,因其在Bean字段注入完成后即执行,此时RedisConnectionFactory等依赖可能未就绪,易导致RedisTemplate为null或抛出RedisConnectionFailureException;CommandLineRunner最稳妥,它在ApplicationContext刷新完成、所有Bean就绪后执行,需加@Order(1)、try-catch包裹并考虑异步处理。

为什么不能用 @PostConstruct 做 Redis 预热
因为 @PostConstruct 在 Bean 字段注入完成后就执行,此时 RedisConnectionFactory 可能还没初始化完毕——尤其在 Spring Boot 3 启用 Lettuce SSL、自定义 RedisCacheConfiguration 或配置了连接池时,RedisTemplate 会是 null,或抛出 RedisConnectionFailureException。常见现象是启动日志里反复出现 Cannot get Jedis connection(即使你用的是 Lettuce),或者预热逻辑静默失败。
CommandLineRunner 是最稳妥的启动后执行点
它在 ApplicationContext 刷新完成、所有单例 Bean(包括 RedisTemplate、JdbcTemplate、业务 @Service)都已就绪后才被调用。注意三点:
- 加
@Order(1)显式控制优先级,避免被其他 Runner 干扰 - 必须包裹
try-catch:预热失败不该导致启动失败,但要记录关键日志,比如“预热失败,跳过 37 条商品数据” - 若预热耗时 >3s(如加载上万条字典项),建议用
new Thread(() -> {...}).start()异步执行;否则可能拖慢/actuator/health探针响应,触发 K8s liveness probe 失败重启
预热数据选型和分批策略直接影响稳定性
不是所有数据都适合启动时预热。重点只加载:
- 访问频次 Top 500 的 key(例如
product:1001、dict:status) - 更新频率
- 单 key value redisTemplate.opsForValue().set() 触发大对象序列化阻塞
对批量数据,别用 for (item : list) redisTemplate.set(...) —— 这会发起 N 次网络调用。改用 redisTemplate.executePipelined() 或 stringRedisTemplate.opsForValue().multiSet() 批量写入。如果数据量超 5000 条,务必分页查库 + 分批缓存,防止 OOM 或 Redis 超时中断。
多实例部署下预热不重复的关键细节
单机预热代码直接上集群会引发 N 倍写入,浪费资源还可能因并发覆盖导致数据不一致。解决方案只有两个:
- 用 Redis 分布式锁(如
RedissonClient.getLock("warmup:lock"))包住整个预热流程,确保仅一个实例执行 - 更轻量的做法:在预热前先
stringRedisTemplate.hasKey("warmup:done"),存在则直接 return;成功后stringRedisTemplate.opsForValue().set("warmup:done", "1", Duration.ofHours(24))
后者依赖 Redis 本身可用,且需注意 key 过期时间要比部署间隔长——比如每天发布两次,那就设成 Duration.ofHours(48)。这个细节漏掉,预热就会在每次重启时重复执行。











