缓存预热(Cache Preheating) 是提升系统启动后性能的关键手段,尤其适用于:
- 系统重启后缓存为空
- 每日定时刷新热点数据
- 大促前预加载商品信息
下面我将为你提供一份 详细、生产级的缓存预热实现方案,适用于 Spring Boot + Redis + Caffeine 的多级缓存架构。
📚 Spring Boot 缓存预热(Cache Preheating)完整实现指南
适用场景:系统启动时、定时任务、大促前预加载热点数据
技术栈:Spring Boot 3 + Redis + Caffeine + Redisson + Scheduled Task
一、什么是缓存预热?
缓存预热 是指在系统启动或特定时间点,主动将热点数据从数据库加载到缓存中,避免首次访问时缓存未命中,导致数据库压力激增。
✅ 典型场景
| 场景 | 说明 |
|---|---|
| 系统重启后 | 避免“缓冷启动”导致雪崩 |
| 每日凌晨 | 预加载昨日热门商品/用户 |
| 大促前(如双11) | 提前加载活动商品信息 |
| 新功能上线 | 预热核心配置数据 |
二、预热策略选择
| 策略 | 说明 | 推荐 |
|---|---|---|
| 启动时预热 | @PostConstruct 或 CommandLineRunner | ✅ 推荐 |
| 定时预热 | @Scheduled 定时任务 | ✅ 推荐 |
| 事件驱动预热 | 监听消息(Kafka/RabbitMQ)触发 | 可选 |
| 手动触发预热 | 提供 API 接口手动调用 | ✅ 建议保留 |
三、实现方式一:系统启动时预热(推荐)
✅ 使用 CommandLineRunner
// service/CachePreheatService.java
package com.example.service;
import com.example.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 缓存预热服务:系统启动时加载热点数据
*/
@Service
public class CachePreheatService implements CommandLineRunner {
@Autowired
private UserService userService;
@Autowired
private RedissonClient redissonClient;
@Override
public void run(String... args) throws Exception {
// 获取分布式锁,防止多个实例重复预热
RLock lock = redissonClient.getLock("cache:preheat:lock");
try {
if (lock.tryLock(10, 60, TimeUnit.SECONDS)) {
System.out.println("开始执行缓存预热...");
preheatHotUsers();
System.out.println("缓存预热完成!");
} else {
System.out.println("其他实例正在预热,跳过...");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 预热热点用户(假设 ID < 1000 的用户为热点)
*/
private void preheatHotUsers() {
for (long id = 1; id <= 1000; id++) {
// 触发 @Cacheable,自动写入 Caffeine 和 Redis
userService.getUser(id);
if (id % 100 == 0) {
Thread.sleep(10); // 避免瞬间压力过大
}
}
}
}
✅ 优势:自动执行,无需人工干预
🔒 安全:使用 Redisson 分布式锁,防止集群环境下重复预热
四、实现方式二:定时预热(每日凌晨)
✅ 使用 @Scheduled
// service/DailyCachePreheatService.java
package com.example.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
/**
* 每日定时缓存预热
*/
@Service
public class DailyCachePreheatService {
@Autowired
private CachePreheatService cachePreheatService;
/**
* 每天凌晨 2:00 执行预热
*/
@Scheduled(cron = "0 0 2 * * ?")
public void preheatDaily() {
System.out.println("开始执行每日缓存预热...");
cachePreheatService.preheatHotUsers();
System.out.println("每日缓存预热完成!");
}
}
⚠️ 注意:需在主类启用定时任务
@SpringBootApplication
@EnableScheduling // 启用定时任务
public class RedisIntegrationApplication {
// ...
}
五、实现方式三:手动触发预热(运维接口)
✅ 提供 REST API
// controller/CacheController.java
@RestController
@RequestMapping("/cache")
public class CacheController {
@Autowired
private CachePreheatService cachePreheatService;
@PostMapping("/preheat")
public String manualPreheat() {
try {
cachePreheatService.run(); // 调用预热逻辑
return "缓存预热已触发";
} catch (Exception e) {
return "预热失败: " + e.getMessage();
}
}
}
可通过 curl 或运维平台调用:
curl -X POST http://localhost:8080/cache/preheat
六、高级预热策略(生产推荐)
✅ 1. 基于访问日志的热点数据识别
-- 示例:从访问日志表中找出昨日访问 Top 100 用户
SELECT user_id, COUNT(*) as hits
FROM user_access_log
WHERE DATE(access_time) = CURDATE() - INTERVAL 1 DAY
GROUP BY user_id
ORDER BY hits DESC
LIMIT 100;
在预热服务中调用此 SQL 加载热点数据。
✅ 2. 分批加载 + 限流
避免一次性加载太多数据压垮 DB 或 Redis:
private void preheatHotUsers() {
int batchSize = 50;
for (int i = 1; i <= 1000; i += batchSize) {
List<Long> batch = IntStream.rangeClosed(i, Math.min(i + batchSize - 1, 1000))
.boxed().toList();
batch.forEach(userService::getUser);
try {
Thread.sleep(100); // 每批间隔 100ms
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
✅ 3. 预热进度监控
@Autowired
private MeterRegistry meterRegistry;
private void preheatHotUsers() {
Counter preheatCounter = meterRegistry.counter("cache.preheat.items");
for (long id = 1; id <= 1000; id++) {
userService.getUser(id);
preheatCounter.increment();
}
}
可在 Prometheus 中查看预热进度。
七、预热数据来源建议
| 数据类型 | 预热策略 |
|---|---|
| 用户信息 | 预热 VIP 用户、昨日活跃用户 |
| 商品信息 | 预热首页推荐、热销商品 |
| 配置项 | 预热所有系统配置(@PostConstruct) |
| 权限菜单 | 登录时预热,或定时刷新 |
| 排行榜 | 每日凌晨预热昨日 Top 100 |
八、注意事项与最佳实践
| 项目 | 建议 |
|---|---|
| 🔐 分布式锁 | 使用 Redisson 锁防止重复预热 |
| 🐢 分批加载 | 避免瞬间压力过大 |
| 📊 监控 | 记录预热耗时、数量 |
| 🧯 降级 | 预热失败不影响系统启动 |
| 🕐 时间选择 | 选择低峰期(如凌晨 2 点) |
| 📦 数据量控制 | 预热数据不宜超过总数据 20% |
九、完整预热流程图
系统启动/定时触发
↓
获取分布式锁(Redisson)
↓
检查是否已预热(可选 Redis 标记)
↓
从 DB/日志 获取热点数据 ID 列表
↓
分批调用业务方法触发缓存(@Cacheable)
↓
更新预热标记 + 监控指标
↓
释放锁
十、总结
| 方式 | 适用场景 | 推荐指数 |
|---|---|---|
| 启动预热 | 系统重启后 | ⭐⭐⭐⭐⭐ |
| 定时预热 | 每日刷新热点 | ⭐⭐⭐⭐☆ |
| 手动预热 | 运维操作 | ⭐⭐⭐⭐☆ |
| 事件预热 | 消息驱动 | ⭐⭐⭐☆☆ |
👉 推荐组合:
✅ 启动预热 + 每日定时预热 + 手动接口
✅ 分布式锁 + 分批加载 + 监控
5万+

被折叠的 条评论
为什么被折叠?



