作为Java生态中最强大的本地缓存解决方案,Caffeine在性能指标上全面超越了传统的Guava Cache和Ehcache。其核心在于采用了创新的Window TinyLFU淘汰算法,相比传统的LRU(最近最少使用)算法,内存命中率提升了30%-50%。这主要得益于其对访问频率和最近访问时间的双重考量。
提示:Window TinyLFU算法通过频率素描(Count-Min Sketch)数据结构高效统计元素访问频率,同时使用滑动窗口机制捕捉最新的访问热点。
在实际压力测试中,Caffeine的吞吐量可达Guava Cache的2-3倍。这源于其精妙的设计:
| 特性 | Caffeine | Guava Cache | Ehcache |
|---|---|---|---|
| 淘汰算法 | TinyLFU | LRU | LRU |
| 最大吞吐量(ops/s) | 1,200万 | 400万 | 300万 |
| 内存占用 | 低 | 中等 | 高 |
| 持久化支持 | ❌ | ❌ | ✔️ |
| 集群支持 | ❌ | ❌ | ✔️ |
这是最基础的缓存模式,适合需要精细控制缓存行为的场景。构建示例如下:
java复制Cache<String, Product> cache = Caffeine.newBuilder()
.initialCapacity(100) // 初始哈希表大小
.maximumSize(10_000) // 基于条目数限制
// 或使用权重限制
// .maximumWeight(50_000)
// .weigher((String key, Product prod) -> prod.getSizeInKB())
.expireAfterWrite(30, TimeUnit.MINUTES) // 写入后过期
.expireAfterAccess(15, TimeUnit.MINUTES) // 访问后过期
.removalListener((String key, Product prod, RemovalCause cause) ->
log.info("产品 {} 被移除,原因:{}", key, cause))
.recordStats() // 开启命中统计
.build();
关键参数说明:
initialCapacity:初始哈希表槽位数,应设置为预估缓存量的1.3倍maximumSize:基于条目数的硬限制,超出时触发淘汰weigher:自定义权重计算函数,与maximumWeight配合使用removalListener:同步执行的移除通知,避免在此执行耗时操作警告:removalListener会阻塞缓存操作线程,如需异步处理应使用
removalListener().async()
当所有数据都可通过某个函数计算得到时,LoadingCache是最佳选择:
java复制LoadingCache<String, Product> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.refreshAfterWrite(5, TimeUnit.MINUTES) // 自动刷新
.build(key -> loadProductFromDB(key)); // 加载函数
// 使用方式
Product p = cache.get("prod_123"); // 不存在时自动加载
Map<String, Product> products = cache.getAll(Arrays.asList("prod_123", "prod_456"));
刷新机制说明:
为充分发挥Caffeine的性能优势,异步缓存是I/O密集型场景的首选:
java复制AsyncLoadingCache<String, Product> asyncCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(30, TimeUnit.MINUTES)
.buildAsync(key -> CompletableFuture.supplyAsync(() ->
loadProductFromDB(key)));
// 使用方式
CompletableFuture<Product> future = asyncCache.get("prod_123");
future.thenAccept(product -> System.out.println(product.getName()));
异步缓存的最佳实践:
asyncCache.synchronous()获取同步视图首先添加依赖(Gradle示例):
groovy复制implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
implementation 'org.springframework.boot:spring-boot-starter-cache'
配置缓存管理器:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CaffeineCacheManager cacheManager() {
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(30, TimeUnit.MINUTES)
.recordStats();
return new CaffeineCacheManager("products", "users") {
@Override
protected Cache<Object, Object> createNativeCache(String name) {
return caffeine.build();
}
};
}
}
java复制@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProduct(String id) {
return productRepository.findById(id); // 实际数据库查询
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(String id) {
productRepository.deleteById(id);
}
@Caching(evict = {
@CacheEvict(value = "products", allEntries = true),
@CacheEvict(value = "recommendations", allEntries = true)
})
public void refreshAllCaches() {
// 清空多个缓存
}
}
多缓存差异化配置:
java复制@Bean
public CacheManager cacheManager() {
SimpleCacheManager manager = new SimpleCacheManager();
List<CaffeineCache> caches = Arrays.asList(
new CaffeineCache("products",
Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build()),
new CaffeineCache("users",
Caffeine.newBuilder()
.maximumSize(500)
.expireAfterAccess(2, TimeUnit.HOURS)
.build())
);
manager.setCaches(caches);
return manager;
}
通过cache.stats()获取的关键指标:
java复制CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate()); // 0.87
System.out.println("平均加载时间: " + stats.averageLoadPenalty() + "ns");
System.out.println("驱逐数量: " + stats.evictionCount());
重要指标说明:
缓存穿透防护:
java复制LoadingCache<String, Optional<Product>> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> {
Product p = loadFromDB(key);
return Optional.ofNullable(p); // 空值也缓存
});
缓存雪崩预防:
java复制.expireAfterWrite(30 + ThreadLocalRandom.current().nextInt(10),
TimeUnit.MINUTES) // 添加随机过期时间
热点key优化:
java复制.refreshAfterWrite(1, TimeUnit.MINUTES) // 自动刷新
.buildAsync(key -> loadProduct(key)); // 异步加载
weakKeys()/weakValues()允许GC回收缓存java复制.serializer(new SnappySerializer()) // 需要额外的序列化依赖
java复制cache.policy().eviction().ifPresent(eviction -> {
eviction.setMaximum(2 * eviction.getMaximum());
});
java复制@Cacheable(key = "T(String).format('%s:%s', #category, #id)")
equals()和hashCode()确保正确性典型的三层缓存架构:
实现示例:
java复制public Product getProduct(String id) {
Product p = l1Cache.getIfPresent(id);
if (p != null) return p;
p = redisTemplate.opsForValue().get("prod:" + id);
if (p != null) {
l1Cache.put(id, p);
return p;
}
p = database.load(id);
redisTemplate.opsForValue().set("prod:" + id, p, 1, TimeUnit.HOURS);
l1Cache.put(id, p);
return p;
}
当数据结构变更时:
java复制@Cacheable(value = "products", key = "'v2:' + #id")
public Product getProductV2(String id) {
// 新版本实现
}
通过实际项目验证,合理配置的Caffeine缓存可以将系统吞吐量提升3-5倍。特别是在商品详情、用户会话等高频访问场景,响应时间从原来的200ms降低到50ms以下。建议结合具体业务特点,通过A/B测试确定最优参数配置。