本文目录导读:

我来为您提供一个完整的Java缓存预热案例,包含多种实现方案。
基础缓存预热架构
1 缓存接口定义
public interface CacheService<K, V> {
V get(K key);
void put(K key, V value);
void clear();
boolean isPreheated();
}
2 本地缓存实现
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class LocalCacheService<K, V> implements CacheService<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
private final AtomicBoolean preheated = new AtomicBoolean(false);
@Override
public V get(K key) {
return cache.get(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
@Override
public void clear() {
cache.clear();
preheated.set(false);
}
@Override
public boolean isPreheated() {
return preheated.get();
}
public void setPreheated(boolean preheated) {
this.preheated.set(preheated);
}
}
Spring Boot 缓存预热方案
1 数据模型
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Product {
private Long id;
private String name;
private Double price;
private Integer stock;
private String category;
private LocalDateTime createTime;
}
2 数据源模拟
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Repository
public class ProductRepository {
private final Map<Long, Product> database = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// 模拟数据库数据
for (long i = 1; i <= 10000; i++) {
Product product = new Product();
product.setId(i);
product.setName("Product-" + i);
product.setPrice(100.0 + i);
product.setStock(100);
product.setCategory("Category-" + (i % 10));
product.setCreateTime(LocalDateTime.now());
database.put(i, product);
}
}
public Product findById(Long id) {
// 模拟数据库查询延迟
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return database.get(id);
}
public List<Product> findAll() {
return new ArrayList<>(database.values());
}
}
3 缓存预热服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class CacheWarmUpService {
@Autowired
private ProductRepository productRepository;
@Autowired
private LocalCacheService<Long, Product> productCache;
// 预热线程池
private final ExecutorService warmUpExecutor = Executors.newFixedThreadPool(10);
// 预热进度
private final AtomicInteger progress = new AtomicInteger(0);
@PostConstruct
public void initialize() {
if (!productCache.isPreheated()) {
warmUpCache();
}
}
/**
* 全量缓存预热
*/
public void warmUpCache() {
log.info("开始缓存预热...");
long startTime = System.currentTimeMillis();
List<Product> allProducts = productRepository.findAll();
int totalSize = allProducts.size();
// 使用CountDownLatch等待所有预热任务完成
CountDownLatch latch = new CountDownLatch(totalSize);
allProducts.forEach(product -> {
warmUpExecutor.submit(() -> {
try {
// 执行预热
productCache.put(product.getId(), product);
progress.incrementAndGet();
} finally {
latch.countDown();
}
});
});
try {
// 等待所有任务完成,设置超时时间
boolean completed = latch.await(5, TimeUnit.MINUTES);
if (completed) {
productCache.setPreheated(true);
long endTime = System.currentTimeMillis();
log.info("缓存预热完成,共加载{}条数据,耗时:{}ms", totalSize, (endTime - startTime));
} else {
log.error("缓存预热超时");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("缓存预热被中断", e);
}
}
/**
* 获取预热进度
*/
public double getProgress() {
return (double) progress.get() / productRepository.findAll().size() * 100;
}
/**
* 检查缓存状态
*/
public boolean isCacheReady() {
return productCache.isPreheated();
}
}
分层预热策略
1 热门数据优先预热
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class SmartCacheWarmUpService {
@Autowired
private ProductRepository productRepository;
@Autowired
private LocalCacheService<Long, Product> productCache;
// 热门商品ID(可以从历史数据中获取)
private final List<Long> hotProductIds = Arrays.asList(1L, 2L, 3L, 4L, 5L, 100L, 200L, 500L);
/**
* 分层预热
* 第一层:热门数据
* 第二层:常规数据
* 第三层:冷门数据
*/
public void tieredWarmUp() {
log.info("开始分层缓存预热...");
// 第一层:预热热门数据(立即执行)
warmUpHotData();
// 第二层:预热常规数据(延迟执行)
scheduleWarmUpNormalData(1000); // 延迟1秒
// 第三层:预热冷门数据(后台执行)
scheduleWarmUpColdData(5000); // 延迟5秒
}
private void warmUpHotData() {
hotProductIds.parallelStream().forEach(id -> {
Product product = productRepository.findById(id);
if (product != null) {
productCache.put(id, product);
log.debug("热门商品缓存预热:{}", id);
}
});
log.info("热门数据预热完成");
}
private void scheduleWarmUpNormalData(long delayMs) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 预热访问频率较高的数据
productRepository.findAll().stream()
.filter(p -> !hotProductIds.contains(p.getId()))
.limit(1000) // 预热前1000条常规数据
.forEach(p -> productCache.put(p.getId(), p));
log.info("常规数据预热完成");
}
}, delayMs);
}
private void scheduleWarmUpColdData(long delayMs) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 后台预热剩余数据
CompletableFuture.runAsync(() -> {
productRepository.findAll().stream()
.filter(p -> productCache.get(p.getId()) == null)
.forEach(p -> productCache.put(p.getId(), p));
log.info("冷数据预热完成");
});
}
}, delayMs);
}
}
健康检查与监控
1 缓存健康检查
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicLong;
@Component
public class CacheHealthChecker {
@Autowired
private LocalCacheService<Long, Product> productCache;
@Autowired
private ProductRepository productRepository;
private final AtomicLong cacheHitCount = new AtomicLong(0);
private final AtomicLong cacheMissCount = new AtomicLong(0);
/**
* 定时检测缓存命中率
*/
@Scheduled(fixedRate = 60000) // 每分钟检测一次
public void checkCacheHealth() {
if (!productCache.isPreheated()) {
log.warn("缓存尚未预热完成");
return;
}
// 随机抽样检测
long testCount = 100;
long hitCount = 0;
for (int i = 0; i < testCount; i++) {
Long randomId = ThreadLocalRandom.current().nextLong(1, 10001);
Product cachedProduct = productCache.get(randomId);
Product dbProduct = productRepository.findById(randomId);
if (cachedProduct != null && cachedProduct.equals(dbProduct)) {
hitCount++;
}
}
double hitRate = (double) hitCount / testCount;
if (hitRate < 0.95) {
log.warn("缓存命中率异常:{}%,需要重新预热", hitRate * 100);
// 触发重新预热
triggerReWarmUp();
} else {
log.info("缓存健康状态良好,命中率:{}%", hitRate * 100);
}
}
private void triggerReWarmUp() {
productCache.clear();
// 执行重新预热逻辑
}
}
启动时自动预热
1 配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.warmup")
public class CacheWarmUpConfig {
private boolean enabled = true;
private int threadPoolSize = 10;
private long timeoutMinutes = 5;
private String strategy = "full"; // full, tiered, hot_only
// getters and setters
}
2 启动监听器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class CacheWarmUpListener implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private CacheWarmUpService warmUpService;
@Autowired
private SmartCacheWarmUpService smartWarmUpService;
@Autowired
private CacheWarmUpConfig warmUpConfig;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if (!warmUpConfig.isEnabled()) {
log.info("缓存预热已禁用");
return;
}
log.info("应用程序已启动,开始执行缓存预热...");
// 根据配置选择预热策略
switch (warmUpConfig.getStrategy()) {
case "full":
warmUpService.warmUpCache();
break;
case "tiered":
smartWarmUpService.tieredWarmUp();
break;
case "hot_only":
smartWarmUpService.warmUpHotData();
break;
default:
warmUpService.warmUpCache();
}
}
}
使用示例
1 application.yml配置
cache:
warmup:
enabled: true
thread-pool-size: 10
timeout-minutes: 5
strategy: tiered
2 业务使用示例
@Service
public class ProductService {
@Autowired
private LocalCacheService<Long, Product> productCache;
@Autowired
private ProductRepository productRepository;
public Product getProductById(Long id) {
// 从缓存获取
Product cachedProduct = productCache.get(id);
if (cachedProduct != null) {
return cachedProduct;
}
// 缓存未命中,从数据库加载
Product dbProduct = productRepository.findById(id);
if (dbProduct != null) {
productCache.put(id, dbProduct);
}
return dbProduct;
}
}
3 REST API监控
@RestController
@RequestMapping("/api/cache")
public class CacheController {
@Autowired
private CacheWarmUpService warmUpService;
@GetMapping("/status")
public ResponseEntity<Map<String, Object>> getCacheStatus() {
Map<String, Object> status = new HashMap<>();
status.put("preheated", warmUpService.isCacheReady());
status.put("progress", warmUpService.getProgress());
return ResponseEntity.ok(status);
}
@PostMapping("/warmup")
public ResponseEntity<String> triggerWarmUp() {
CompletableFuture.runAsync(() -> warmUpService.warmUpCache());
return ResponseEntity.accepted().body("缓存预热已启动");
}
}
这个缓存预热方案包含了:
- 多种预热策略:全量预热、分层预热、热点优先
- 并发控制:使用线程池和CountDownLatch
- 健康监控:定时检查缓存命中率
- 失败处理:超时机制和重试逻辑
- 配置灵活:通过配置文件控制预热行为
- 启动自动执行:利用Spring事件机制
你可以根据实际业务需求选择合适的预热策略,并调整线程池大小、超时时间等参数。