Guava Cache 高性能本地缓存库详解与使用案例
Guava Cache 高性能本地缓存库详解与使用案例
一、Guava Cache 简介
1.1 什么是 Guava Cache?
Guava Cache 是 Google 提供的 Java 本地缓存库,是 Google Guava 工具库的一部分。它提供了灵活且高效的缓存管理功能,适用于需要高性能、低延迟的本地缓存场景。核心特性包括:
- 基于大小和时间的缓存驱逐
- 软引用/弱引用支持
- 缓存统计(命中率、加载次数等)
- 手动刷新与异步加载
- 缓存监听器
Guava Cache 适用于单体应用、微服务中的本地缓存需求,尤其适合对缓存性能要求较高的场景。
二、核心特性详解
2.1 依赖引入
Maven
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
Gradle
implementation 'com.google.guava:guava:31.1-jre'
2.2 缓存构建器 CacheBuilder
Guava Cache 的核心是通过 CacheBuilder
构建缓存实例,支持链式配置。
2.2.1 基本缓存创建
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)// 最大缓存大小
.build();
2.2.2 基于时间的过期策略
- 写入后过期(expireAfterWrite):元素在写入后指定时间过期。
- 访问后过期(expireAfterAccess):元素在最后一次访问后指定时间过期。
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)// 写入后10分钟过期
.expireAfterAccess(5, TimeUnit.MINUTES)// 最后一次访问后5分钟过期
.build();
2.2.3 基于大小的驱逐策略
Guava 使用 LRU(Least Recently Used) 算法管理缓存大小。
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1000)// 缓存最多存储1000个元素
.build();
2.2.4 软引用/弱引用
- 软引用(Soft References):在 JVM 内存不足时回收。
- 弱引用(Weak References):在下一次 GC 时回收。
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.softValues()// 值使用软引用
.weakKeys()// 键使用弱引用
.build();
2.2.5 缓存统计
启用统计功能可获取命中率、加载次数等信息。
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.recordStats()// 启用统计
.build();// 获取统计信息
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate());
2.2.6 手动刷新与异步加载
- 手动刷新:
refresh(key)
强制刷新指定缓存项。 - 异步加载:结合
LoadingCache
实现异步加载。
LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
.maximumSize(100)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return fetchFromDB(key);// 从数据库加载
}
});// 异步加载
String value = loadingCache.get("key");
2.3 缓存监听器
通过 RemovalListener
监听缓存项被驱逐或过期。
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.removalListener((RemovalListener<String, String>) notification -> {
System.out.println("Key: " + notification.getKey() + " 被移除,原因: " + notification.getCause());
})
.build();
三、使用案例详解
3.1 单体应用缓存
场景:用户信息缓存
public class UserService {
private final LoadingCache<String, User> userCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, User>() {
@Override
public User load(String userId) throws Exception {
return fetchUserFromDB(userId);// 从数据库加载
}
});public User getUser(String userId) {
try {
return userCache.get(userId);// 自动加载或从缓存获取
} catch (ExecutionException e) {
throw new RuntimeException("缓存加载失败", e);
}
}private User fetchUserFromDB(String userId) {
// 模拟数据库查询
return new User(userId, "User_" + userId);
}
}
优势:
- 减少数据库压力:高频查询直接命中缓存。
- 自动刷新:Guava 自动管理缓存更新。
3.2 分布式系统中的本地缓存
场景:结合 Redis 的二级缓存
Guava Cache 作为本地缓存,Redis 作为分布式缓存。
public class DistributedCache {
private final Cache<String, String> localCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();public String get(String key) {
String value = localCache.getIfPresent(key);
if (value == null) {
value = fetchFromRedis(key);
if (value == null) {
value = fetchFromDB(key);
saveToRedis(key, value);
}
localCache.put(key, value);
}
return value;
}
}
优势:
- 本地缓存加速:减少对 Redis 的直接访问。
- 降级容错:Redis 不可用时可直接访问数据库。
3.3 Spring Boot 集成
1. 配置缓存 Bean
@Configuration
public class CacheConfig {
@Bean
public Cache<String, String> myCache() {
return CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
}
}
2. 使用缓存
@Service
public class MyService {
@Autowired
private Cache<String, String> myCache;public String getData(String key) {
String value = myCache.getIfPresent(key);
if (value == null) {
value = fetchFromExternalService(key);
myCache.put(key, value);
}
return value;
}
}
四、性能优化技巧
4.1 避免缓存雪崩
- 随机过期时间:为缓存设置随机过期时间,避免大量缓存同时失效。
- 热点数据预加载:对高频访问的数据主动加载到缓存。
// 随机过期时间(需结合自定义逻辑)
Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build();
4.2 缓存命中率优化
- 合理设置大小:根据业务访问模式调整
maximumSize
。 - 监控统计:定期分析
hitRate()
和evictionCount()
,调整策略。
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate());
4.3 内存管理
- 软引用值(softValues):在 JVM 内存不足时自动回收缓存。
- 定期清理:使用
cleanUp()
手动触发清理。
cache.cleanUp(); // 手动清理过期缓存
五、常见问题与解决方案
5.1 缓存穿透(Cache Penetration)
问题:查询不存在的数据导致频繁访问数据库。
解决方案:
- 布隆过滤器(Bloom Filter):拦截不存在的键。
- 空值缓存:对查询结果为空的键设置短暂缓存。
String value = cache.get(key, () -> {
String result = fetchFromDB(key);
if (result == null) {
cache.put(key, "");// 缓存空值
}
return result;
});
5.2 缓存击穿(Cache Breakdown)
问题:热点数据过期后大量请求直接访问数据库。
解决方案:
- 互斥锁(Mutex Lock):仅允许一个线程重建缓存。
- 永不过期:热点数据设置永不过期,定时异步更新。
String value = cache.get(key, () -> {
synchronized (key.intern()) {
if (cache.getIfPresent(key) == null) {
String result = fetchFromDB(key);
cache.put(key, result);
return result;
}
}
return cache.getIfPresent(key);
});
5.3 缓存雪崩(Cache Avalanche)
问题:大量缓存同时失效,导致数据库压力激增。
解决方案:
- 随机过期时间:为缓存设置随机过期时间。
- 分层缓存:本地缓存 + Redis 分布式缓存。
六、Guava Cache 与 Caffeine 对比
特性 | Guava Cache | Caffeine |
---|---|---|
算法 | LRU(较简单) | Window TinyLFU(更高命中率) |
异步加载 | 不支持(需手动实现) | 支持 AsyncLoadingCache |
统计功能 | 基本统计 | 详细统计(命中率、加载次数等) |
性能 | 一般(适合中等规模应用) | 更高吞吐量和更低延迟 |
维护状态 | 已停止更新(推荐使用 Caffeine) | 活跃维护 |
七、总结
Guava Cache 是 Java 生态中经典的本地缓存库,其 灵活配置、稳定性和易用性 使其成为许多项目的首选。通过合理配置缓存策略,开发者可以显著提升系统性能,减少对数据库的依赖。尽管 Caffeine 在性能上更优,但 Guava Cache 仍然是许多遗留项目和中等规模应用的可靠选择。
八、参考资料
- Guava 官方文档:https://github.com/google/guava
- Guava Cache 最佳实践:https://www.baeldung.com/java-guava-cache
- Guava 与 Caffeine 对比:https://github.com/ben-manes/caffeine/wiki/Guava