Java缓存资源优化案例详解
典型场景:高并发商品查询系统
问题描述
电商系统中,商品详情页面临高并发查询,每次查询都直接访问数据库导致性能瓶颈。

优化方案实现
本地缓存(Caffeine)
@Service
public class ProductService {
// 配置Caffeine缓存
private final Cache<String, Product> productCache = Caffeine.newBuilder()
.maximumSize(10000) // 最大缓存条目
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期
.recordStats() // 记录统计信息
.build();
@Autowired
private ProductRepository productRepository;
public Product getProduct(String productId) {
// 先从缓存获取
Product product = productCache.getIfPresent(productId);
if (product != null) {
return product;
}
// 缓存未命中,从数据库加载
product = productRepository.findById(productId)
.orElseThrow(() -> new ProductNotFoundException(productId));
// 放入缓存
productCache.put(productId, product);
return product;
}
// 缓存统计监控
public CacheStats getCacheStats() {
return productCache.stats();
}
}
Redis分布式缓存
@Service
public class ProductCacheService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ObjectMapper objectMapper;
// 缓存Key前缀
private static final String PRODUCT_CACHE_PREFIX = "product:";
private static final long CACHE_TTL = 30; // 分钟
public Product getProduct(String productId) {
String cacheKey = PRODUCT_CACHE_PREFIX + productId;
// 1. 查询Redis缓存
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
if (cacheValue != null) {
// 缓存命中
return deserializeProduct(cacheValue);
}
// 2. 缓存未命中,查询数据库
Product product = queryFromDatabase(productId);
if (product == null) {
// 3. 防止缓存穿透:存储空值
redisTemplate.opsForValue().set(cacheKey, "NULL", 5, TimeUnit.MINUTES);
return null;
}
// 4. 更新缓存
redisTemplate.opsForValue().set(cacheKey,
serializeProduct(product),
CACHE_TTL,
TimeUnit.MINUTES);
return product;
}
// 缓存预热
@PostConstruct
public void preloadCache() {
List<Product> hotProducts = productRepository.findHotProducts();
hotProducts.forEach(product -> {
String cacheKey = PRODUCT_CACHE_PREFIX + product.getId();
redisTemplate.opsForValue().set(cacheKey,
serializeProduct(product),
CACHE_TTL,
TimeUnit.MINUTES);
});
}
}
多级缓存架构
@Service
public class MultiLevelCacheService {
// 一级缓存:本地堆缓存 (Caffeine)
private final Cache<String, Product> localCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
// 二级缓存:Redis缓存
@Autowired
private RedisTemplate<String, Product> redisTemplate;
@Autowired
private ProductRepository productRepository;
public Product getProduct(String productId) {
String cacheKey = "product:" + productId;
// 第一步:查询本地缓存
Product product = localCache.getIfPresent(productId);
if (product != null) {
return product;
}
// 第二步:查询Redis缓存
product = redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
// 回填本地缓存
localCache.put(productId, product);
return product;
}
// 第三步:查询数据库
product = productRepository.findById(productId)
.orElse(null);
if (product != null) {
// 回填Redis和本地缓存
redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
localCache.put(productId, product);
}
return product;
}
}
缓存更新策略
主动更新策略
@Component
public class CacheUpdateStrategy {
@Autowired
private Cache<String, Product> localCache;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitListener(queues = "product.update.queue")
public void handleProductUpdate(ProductUpdateEvent event) {
// 1. 更新数据库
productRepository.save(event.getProduct());
// 2. 删除缓存(下次查询自动加载)
String cacheKey = "product:" + event.getProductId();
localCache.invalidate(event.getProductId());
redisTemplate.delete(cacheKey);
// 3. 发送缓存失效消息到其他服务节点
sendCacheInvalidationMessage(event.getProductId());
}
}
缓存优化监控
@Component
public class CacheMonitor {
@Autowired
private MeterRegistry meterRegistry;
// 缓存命中率监控
public void recordCacheHit(String cacheName) {
meterRegistry.counter("cache.hits", "cache", cacheName).increment();
}
public void recordCacheMiss(String cacheName) {
meterRegistry.counter("cache.misses", "cache", cacheName).increment();
}
// 缓存大小监控
@Scheduled(fixedRate = 60000) // 每分钟
public void logCacheStats() {
CacheStats stats = productCache.stats();
log.info("Cache Stats - Hit Rate: {}, Eviction Count: {}, Load Time: {}",
stats.hitRate(),
stats.evictionCount(),
stats.averageLoadPenalty());
}
}
性能对比数据
| 指标 | 无缓存 | 本地缓存 | Redis缓存 | 多级缓存 |
|---|---|---|---|---|
| 平均响应时间 | 200ms | 5ms | 15ms | 3ms |
| QPS | 500 | 50000 | 20000 | 80000 |
| 数据库压力 | 高 | 低 | 中 | 极低 |
| 缓存成本 | 无 | 低 | 中 | 中高 |
最佳实践建议
- 选择合适的缓存粒度:针对不同场景选择合适的缓存对象大小
- 设置合理的过期时间:避免缓存雪崩,使用随机过期时间
- 实现缓存预热:系统启动时加载热点数据
- 监控缓存命中率:低于80%需要优化缓存策略
- 处理缓存穿透:缓存空值或布隆过滤器
- 防止缓存击穿:使用互斥锁控制并发访问
常见问题解决
// 缓存穿透防护:布隆过滤器
@Component
public class BloomFilterCache {
private BloomFilter<String> bloomFilter;
@PostConstruct
public void init() {
// 初始化布隆过滤器,预期数据量100万,误判率0.01
bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),
1000000, 0.01);
}
public boolean mightContain(String key) {
return bloomFilter.mightContain(key);
}
}
// 缓存击穿防护:互斥锁
public Product getProductWithLock(String productId) {
Product product = cache.get(productId);
if (product == null) {
synchronized (this) {
product = cache.get(productId);
if (product == null) {
product = db.query(productId);
cache.put(productId, product);
}
}
}
return product;
}
通过以上优化,可以将系统性能提升10-100倍,显著改善用户体验和系统稳定性。