目录
- 简单高效的缓存解决方案--Guava Cache
- 什么是 Guava Cache?
- 核心优势
- 1. 自动驱逐策略
- 2. 时间-based 过期
- 3. 自动加载数据
- 实战示例
- 1. 配置缓存实例
- 2. 批量操作
- 3. 缓存统计与监控
- 高级特性
- 1. 缓存刷新
- 2. 移除监听器
- 3. 异步刷新
- 最佳实践
- 1. 合理设置缓存大小
- 2. 处理加载异常
- 3. 结合 Spring 使用
- 性能考虑
- 1. 选择合适的驱逐策略
- 2. 监控与调优
- 总结
简单高效的缓存解决方案--Guava Cache
在构建高性能Java应用时,缓存是提升系统性能的关键技术之一。虽然 ConcurrentHashMap 能够提供基础的键值存储,但在实际的缓存场景中,我们往往需要更多专业特性。这就是 com.google.common.cache 大显身手的地方。
什么是 Guava Cache?
Guava Cache 是 Google Guava 库中的一个本地缓存实现,它提供了比普通 Map 更丰富的缓存特性,包括自动驱逐、过期策略、统计信息等,专门为缓存场景深度优化。
Guava Cache 是超越普通Map的高性能本地缓存。
从本质上讲,它确实就是一个 Map。
但更准确地说,它是一个「智能的、自带管理功能的 Map」。
<dependencies><!-- Guava 核心依赖 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.3-jre</version><!-- 或者使用 Android 版本 --><!-- <version>32.1.3-android</version> --></dependency>
</dependencies>
核心优势
1. 自动驱逐策略
普通 Map 需要手动管理内存,而 Guava Cache 提供了多种自动驱逐机制:
// 基于大小的驱逐
Cache<String, Object> sizeBasedCache = CacheBuilder.newBuilder().maximumSize(1000) // 最多1000个条目.build();// 基于权重的驱逐
Cache<String, Object> weightBasedCache = CacheBuilder.newBuilder().maximumWeight(1000000) // 总权重最大100万.weigher((String key, Object value) -> getWeight(value)).build();
2. 时间-based 过期
Cache<String, Object> timeBasedCache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期.expireAfterAccess(5, TimeUnit.MINUTES) // 5分钟未被访问则过期.build();
3. 自动加载数据
最强大的特性之一:当缓存未命中时自动加载数据。
LoadingCache<String, User> userCache = CacheBuilder.newBuilder().maximumSize(1000).build(new CacheLoader<String, User>() {@Overridepublic User load(String userId) throws Exception {// 当缓存不存在时,自动调用此方法加载数据return userService.getUserById(userId);}});// 使用 - 如果不存在会自动加载
User user = userCache.get("123");
实战示例
1. 配置缓存实例
public class ProductCache {private final LoadingCache<String, Product> cache;public ProductCache(ProductService productService) {this.cache = CacheBuilder.newBuilder().maximumSize(500).expireAfterWrite(30, TimeUnit.MINUTES).refreshAfterWrite(10, TimeUnit.MINUTES) // 定时刷新.recordStats() // 开启统计.build(new CacheLoader<String, Product>() {@Overridepublic Product load(String productId) {return productService.getProduct(productId);}@Overridepublic ListenableFuture<Product> reload(String key, Product oldValue) {// 异步刷新return productService.getProductAsync(key);}});}public Product getProduct(String productId) {try {return cache.get(productId);} catch (ExecutionException e) {throw new RuntimeException("Failed to load product: " + productId, e);}}
}
2. 批量操作
// 批量获取
Map<String, User> users = userCache.getAll(Arrays.asList("1", "2", "3"));// 批量失效
cache.invalidateAll(Arrays.asList("key1", "key2"));// 清空缓存
cache.invalidateAll();
3. 缓存统计与监控
CacheStats stats = cache.stats();logger.info("缓存命中率: {}%", stats.hitRate() * 100);
logger.info("平均加载时间: {}ms", stats.averageLoadPenalty() / 1000000);
logger.info("缓存命中次数: {}", stats.hitCount());
logger.info("缓存未命中次数: {}", stats.missCount());
logger.info("驱逐次数: {}", stats.evictionCount());
高级特性
1. 缓存刷新
LoadingCache<String, Config> configCache = CacheBuilder.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES) // 1分钟后刷新.build(new CacheLoader<String, Config>() {@Overridepublic Config load(String key) {return loadConfig(key);}});
2. 移除监听器
Cache<String, Data> cache = CacheBuilder.newBuilder().removalListener((RemovalNotification<String, Data> notification) -> {RemovalCause cause = notification.getCause();String key = notification.getKey();Data value = notification.getValue();logger.info("Key {} 被移除,原因: {}", key, cause);if (cause == RemovalCause.SIZE) {// 处理因大小限制被移除的情况metrics.recordEviction();}}).build();
3. 异步刷新
LoadingCache<String, HeavyObject> asyncCache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES).build(CacheLoader.asyncReloading(new CacheLoader<String, HeavyObject>() {@Overridepublic HeavyObject load(String key) {return computeHeavyObject(key);}}, executorService // 使用线程池异步刷新));
最佳实践
1. 合理设置缓存大小
// 根据应用内存情况设置合理的大小
CacheBuilder.newBuilder().maximumSize(calculateOptimalSize()) // 动态计算.build();
2. 处理加载异常
LoadingCache<String, Data> safeCache = CacheBuilder.newBuilder().build(new CacheLoader<String, Data>() {@Overridepublic Data load(String key) throws Exception {try {return externalService.getData(key);} catch (Exception e) {// 返回默认值或抛出特定异常return getDefaultData();}}});
3. 结合 Spring 使用
@Component
public class CacheManager {@Autowiredprivate UserRepository userRepository;private LoadingCache<Long, User> userCache;@PostConstructpublic void init() {userCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(2, TimeUnit.HOURS).recordStats().build(CacheLoader.from(userRepository::findById));}public User getUser(Long id) {try {return userCache.get(id);} catch (ExecutionException e) {throw new RuntimeException("Cache load failed", e);}}
}
性能考虑
1. 选择合适的驱逐策略
- maximumSize(): 适用于条目大小相近的场景
- maximumWeight(): 适用于条目大小差异较大的场景
- expireAfterWrite(): 适用于数据不经常变化的场景
- expireAfterAccess(): 适用于需要保留热门数据的场景
2. 监控与调优
// 定期输出缓存统计
scheduledExecutorService.scheduleAtFixedRate(() -> {CacheStats stats = cache.stats();if (stats.requestCount() > 0) {logger.info("缓存统计: 命中率={}%, 加载次数={}, 平均加载时间={}ms",String.format("%.2f", stats.hitRate() * 100),stats.loadCount(),String.format("%.2f", stats.averageLoadPenalty() / 1000000.0));}
}, 1, 1, TimeUnit.MINUTES);
总结
Guava Cache 提供了一个功能完整、性能优异的本地缓存解决方案。与普通 Map 相比,它在以下方面表现出色:
- ✅ 自动内存管理 - 无需手动清理
- ✅ 灵活的过期策略 - 时间和访问频率控制
- ✅ 丰富的统计信息 - 便于监控和调优
- ✅ 线程安全 - 内置并发控制
- ✅ 自动加载 - 简化缓存未命中处理
对于大多数Java应用的本地缓存需求,Guava Cache 都是一个值得信赖的选择。它既保持了使用的简洁性,又提供了企业级缓存所需的强大功能。
适用场景:数据量不大、访问频繁、计算成本高的数据缓存,如配置信息、用户会话、数据库查询结果等。
不适用场景:分布式环境、超大容量缓存(考虑 Redis 等分布式缓存)。