Caffeine 是一个高性能的 Java 缓存库,其 LoadingCache 提供了自动加载和缓存管理功能。以下是使用 Caffeine LoadingCache 时的一些建议:
1. 合理配置缓存参数
- 初始容量:通过
initialCapacity设置合理的初始容量,避免频繁扩容。 - 最大容量:使用
maximumSize或maximumWeight限制缓存大小,防止内存溢出。 - 过期策略:
- 时间过期:
expireAfterWrite(写入后过期)或expireAfterAccess(访问后过期)。 - 权重策略:对缓存项自定义权重(通过
weigher),适用于不同大小的缓存对象。
- 时间过期:
LoadingCache<Key, Value> cache = Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> loadDataFromDB(key));
2. 避免缓存击穿与雪崩
- 同步加载:使用
LoadingCache的get方法自动加载数据,天然避免击穿(同一 Key 并发请求只触发一次加载)。 - 异步加载:通过
refreshAfterWrite设置定时刷新,结合AsyncLoadingCache异步加载,减少阻塞。 - 随机过期时间:对过期时间添加随机偏移(如
±10%),避免大量缓存同时失效(雪崩)。
AsyncLoadingCache<Key, Value> asyncCache = Caffeine.newBuilder()
.refreshAfterWrite(5, TimeUnit.MINUTES)
.buildAsync(key -> loadDataAsync(key));
3. 缓存预热
- 启动时预加载:在系统初始化阶段主动加载热点数据,避免首次请求延迟。
- 批量加载:使用
getAll方法批量加载数据,减少多次 IO 开销。
// 预热单个 Key
cache.get(hotKey);
// 预热多个 Key
cache.getAll(hotKeys);
4. 处理缓存穿透
- 空值占位:对不存在的 Key 缓存空值(如
Optional.empty()),避免频繁查询底层数据源。 - 布隆过滤器:结合布隆过滤器(Bloom Filter)快速判断 Key 是否存在。
LoadingCache<Key, Optional<Value>> cache = Caffeine.newBuilder()
.build(key -> {
Value value = loadDataFromDB(key);
return Optional.ofNullable(value);
});
5. 异常处理与降级
- 捕获加载异常:在
CacheLoader中处理异常,避免缓存污染。 - 降级策略:返回默认值或旧数据,保证系统可用性。
LoadingCache<Key, Value> cache = Caffeine.newBuilder()
.build(new CacheLoader<Key, Value>() {
@Override
public Value load(Key key) {
try {
return loadDataFromDB(key);
} catch (Exception e) {
return getDefaultValue();
}
}
});
6. 监控与调优
- 开启统计:通过
recordStats()收集命中率、加载时间等指标。 - 动态调整:根据监控数据优化参数(如最大容量、过期时间)。
LoadingCache<Key, Value> cache = Caffeine.newBuilder()
.recordStats()
.build(key -> loadData(key));
// 获取统计信息
CacheStats stats = cache.stats();
double hitRate = stats.hitRate();
7. 结合读写策略
- Write-Through:通过
CacheWriter实现缓存与数据源的同步更新。 - Write-Behind:异步批量更新数据源(需结合外部队列或线程池)。
CacheWriter<Key, Value> writer = new CacheWriter<>() {
@Override
public void write(Key key, Value value) {
// 同步写入数据库
saveToDB(key, value);
}
};
LoadingCache<Key, Value> cache = Caffeine.newBuilder()
.writer(writer)
.build(key -> loadData(key));
8. 线程安全与资源释放
- 线程安全:Caffeine 缓存本身是线程安全的,但
CacheLoader的实现需保证幂等性。 - 关闭缓存:在应用关闭时调用
cache.cleanUp()或cache.invalidateAll()释放资源。
适用场景建议
- 高频读、低频写:适合缓存静态或半静态数据(如配置信息)。
- 计算成本高:缓存复杂计算结果(如机器学习模型输出)。
- 保护底层系统:减少对数据库或 API 的频繁调用。
以上配置可以显著提升缓存命中率、降低延迟,同时避免常见问题(击穿、雪崩、穿透)。建议结合具体业务场景调整参数,并通过压力测试验证效果。