一文詳解Caffeine緩存庫的常用功能與使用指南
Caffeine作為新一代高性能Java緩存庫,在并發(fā)場景下展現(xiàn)出卓越表現(xiàn)。它通過創(chuàng)新的W-TinyLFU淘汰算法實現(xiàn)高達99%的命中率,并采用無鎖設計使吞吐量較傳統(tǒng)方案提升5-10倍。該庫提供靈活的緩存管理能力:支持基于時間(寫入/訪問過期)、數(shù)量或權(quán)重的淘汰策略;允許為單個Key設置專屬過期時間;獨創(chuàng)的異步刷新機制能在不阻塞請求的情況下更新數(shù)據(jù)。開發(fā)者可通過簡潔的鏈式API配置內(nèi)存控制、加載邏輯和事件監(jiān)聽,輕松構(gòu)建高并發(fā)低延遲的智能緩存系統(tǒng)。其與Guava Cache兼容的接口設計,更使遷移成本降至最低。
以下是 Caffeine 緩存庫的常用功能使用介紹,涵蓋基礎操作、過期策略、淘汰配置等核心功能:
一、基礎緩存操作
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
// 1. 創(chuàng)建緩存實例
Cache<String, Object> cache = Caffeine.newBuilder()
.build();
// 2. 添加數(shù)據(jù)
cache.put("key1", "value1");
// 3. 獲取數(shù)據(jù)(手動)
Object value = cache.getIfPresent("key1"); // 存在返回value,否則null
// 4. 刪除數(shù)據(jù)
cache.invalidate("key1");
cache.invalidateAll(); // 清空緩存
二、自動加載緩存(推薦)
LoadingCache<String, Object> cache = Caffeine.newBuilder()
.build(key -> {
// 緩存未命中時自動執(zhí)行的加載邏輯
return fetchFromDB(key); // 自定義數(shù)據(jù)庫加載方法
});
// 自動加載數(shù)據(jù)(緩存未命中時執(zhí)行build中的邏輯)
Object value = cache.get("user_123");
三、過期策略配置
1. 全局過期策略
Cache<String, Object> cache = Caffeine.newBuilder()
// 寫入后30分鐘過期
.expireAfterWrite(30, TimeUnit.MINUTES)
// 最后訪問后15分鐘過期
.expireAfterAccess(15, TimeUnit.MINUTES)
// 自定義過期策略(按需實現(xiàn))
.expireAfter(new Expiry<String, Object>() {
public long expireAfterCreate(String key, Object value, long currentTime) {
return TimeUnit.MINUTES.toNanos(10); // 創(chuàng)建后10分鐘過期
}
public long expireAfterUpdate(String key, Object value, long currentTime, long currentDuration) {
return currentDuration; // 更新后不改變過期時間
}
public long expireAfterRead(String key, Object value, long currentTime, long currentDuration) {
return currentDuration; // 讀取后不改變過期時間
}
})
.build();
2. 單Key過期(高級用法)
// 創(chuàng)建支持變長過期的緩存
Cache<String, Object> cache = Caffeine.newBuilder()
.expireAfter(new Expiry<String, Object>() {
// ...實現(xiàn)同上
})
.build();
// 為特定Key設置不同過期時間
cache.policy().expireVariably().ifPresent(policy -> {
policy.put("hot_key", "value", 2, TimeUnit.HOURS); // 2小時
policy.put("cold_key", "value", 10, TimeUnit.MINUTES); // 10分鐘
});
四、淘汰策略配置
Cache<String, Object> cache = Caffeine.newBuilder()
// 基于數(shù)量淘汰(最多1000個條目)
.maximumSize(1000)
// 基于權(quán)重淘汰(需實現(xiàn)Weigher)
.maximumWeight(10_000)
.weigher((String key, Object value) -> {
// 自定義權(quán)重計算邏輯
if (value instanceof String) return ((String) value).length();
if (value instanceof List) return ((List<?>) value).size();
return 1;
})
// 基于引用回收(謹慎使用)
.weakKeys() // 弱引用Key
.weakValues() // 弱引用Value
.softValues() // 軟引用Value
.build();
五、刷新策略(優(yōu)于純過期)
LoadingCache<String, Object> cache = Caffeine.newBuilder()
// 寫入后1分鐘可刷新(不阻塞讀取,異步刷新舊值)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(key -> fetchFromDB(key));
六、監(jiān)聽器與統(tǒng)計
Cache<String, Object> cache = Caffeine.newBuilder()
// 移除監(jiān)聽器
.removalListener((String key, Object value, RemovalCause cause) -> {
System.out.printf("Key %s was removed (%s)%n", key, cause);
})
// 啟用統(tǒng)計
.recordStats()
.build();
// 獲取統(tǒng)計信息
CacheStats stats = cache.stats();
System.out.printf("Hit Rate: %.2f%%, Loads: %d%n",
stats.hitRate() * 100, stats.loadCount());
七、異步操作
// 1. 異步緩存
AsyncLoadingCache<String, Object> asyncCache = Caffeine.newBuilder()
.buildAsync(key -> fetchFromDB(key));
// 獲取數(shù)據(jù)(返回CompletableFuture)
CompletableFuture<Object> future = asyncCache.get("key1");
// 2. 同步視圖操作
Object value = asyncCache.synchronous().get("key1");
八、最佳實踐配置模板
LoadingCache<String, Object> optimalCache = Caffeine.newBuilder()
// 容量控制
.maximumSize(10_000)
// 過期策略
.expireAfterWrite(30, TimeUnit.MINUTES)
// 刷新策略
.refreshAfterWrite(5, TimeUnit.MINUTES)
// 統(tǒng)計和監(jiān)聽
.recordStats()
.removalListener((key, value, cause) ->
logRemoval(key, cause))
// 自動加載
.build(key -> fetchFromDB(key));
九、基于Caffeine實現(xiàn)的動態(tài)緩存
我們有時候需要這樣一種場景:當用戶請求某個key的時候,該緩存自動從數(shù)據(jù)庫去加載,就是注冊一個數(shù)據(jù)庫加載器(自己實現(xiàn)),當獲取不到該key時,自動走數(shù)據(jù)庫查詢,然后存入該key中。當往caffeine緩存中插入一個key后,如果緩存沒有,則自動存入,并自動同步到數(shù)據(jù)庫中,當刪除一個key,或key過期后,自動從數(shù)據(jù)庫同步刪除。
以下是簡單的實現(xiàn)流程:
import com.github.benmanes.caffeine.cache.*;
import java.util.concurrent.TimeUnit;
public class DynamicCache<K, V> {
private final Cache<K, V> cache;
private final DataLoader<K, V> dataLoader;
private final DataSynchronizer<K, V> dataSynchronizer;
public DynamicCache(DataLoader<K, V> dataLoader, DataSynchronizer<K, V> dataSynchronizer) {
this.dataLoader = dataLoader;
this.dataSynchronizer = dataSynchronizer;
this.cache = Caffeine.newBuilder()
// 配置緩存策略(按需設置)
.expireAfterWrite(30, TimeUnit.MINUTES) // 30分鐘過期
.maximumSize(1000) // 最大緩存項
// 注冊移除監(jiān)聽器(用于刪除數(shù)據(jù)庫數(shù)據(jù))
.removalListener((K key, V value, RemovalCause cause) -> {
if (cause.wasEvicted()) { // 僅處理過期或容量剔除
dataSynchronizer.deleteFromDatabase(key);
}
})
// 注冊加載器(用于緩存未命中時從DB加載)
.build(key -> {
V value = dataLoader.loadFromDatabase(key);
if (value == null) throw new Exception("Key not found");
return value;
});
}
// 獲取數(shù)據(jù)(自動加載)
public V get(K key) {
return cache.get(key, k -> {
V value = dataLoader.loadFromDatabase(k);
if (value == null) throw new RuntimeException("Data not found");
return value;
});
}
// 添加/更新數(shù)據(jù)(同步到數(shù)據(jù)庫)
public void put(K key, V value) {
// 先同步到數(shù)據(jù)庫
dataSynchronizer.saveToDatabase(key, value);
// 再更新緩存
cache.put(key, value);
}
// 刪除數(shù)據(jù)(同步刪除數(shù)據(jù)庫)
public void delete(K key) {
// 先刪除數(shù)據(jù)庫數(shù)據(jù)
dataSynchronizer.deleteFromDatabase(key);
// 再使緩存失效
cache.invalidate(key);
}
// 數(shù)據(jù)庫加載器接口
public interface DataLoader<K, V> {
V loadFromDatabase(K key);
}
// 數(shù)據(jù)庫同步器接口
public interface DataSynchronizer<K, V> {
void saveToDatabase(K key, V value);
void deleteFromDatabase(K key);
}
}
上述接口使用示例:
// 1. 實現(xiàn)數(shù)據(jù)庫操作接口
DynamicCache.DataLoader<String, User> loader = key ->
jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", User.class, key);
DynamicCache.DataSynchronizer<String, User> synchronizer = new DynamicCache.DataSynchronizer<>() {
@Override
public void saveToDatabase(String key, User value) {
jdbcTemplate.update("INSERT OR REPLACE INTO users (id, name) VALUES (?, ?)",
key, value.getName());
}
@Override
public void deleteFromDatabase(String key) {
jdbcTemplate.update("DELETE FROM users WHERE id=?", key);
}
};
// 2. 創(chuàng)建緩存實例
DynamicCache<String, User> userCache = new DynamicCache<>(loader, synchronizer);
// 3. 使用緩存
// 自動加載(緩存未命中時從DB加載)
User user = userCache.get("user123");
// 添加/更新(同步到DB)
userCache.put("user456", new User("Alice"));
// 刪除(同步刪除DB數(shù)據(jù))
userCache.delete("user789");
關鍵特性說明:
1.自動加載:
- 當調(diào)用
get()方法且緩存未命中時,自動通過DataLoader從數(shù)據(jù)庫加載 - 加載成功后自動填充緩存
2.寫穿透:
put() 操作時:
- 先通過
DataSynchronizer保存到數(shù)據(jù)庫 - 再更新緩存
保證數(shù)據(jù)庫與緩存的數(shù)據(jù)一致性
3.刪除同步:
delete() 操作時:
- 先刪除數(shù)據(jù)庫數(shù)據(jù)
- 再使緩存失效
緩存過期/淘汰時:
通過 RemovalListener 自動觸發(fā)數(shù)據(jù)庫刪除
4.緩存配置:
- 可自定義過期時間(
expireAfterWrite) - 可設置最大容量(
maximumSize) - 支持其他Caffeine特性(刷新策略、弱引用等)
關鍵特性對比表
| 功能 | 配置方法 | 適用場景 |
|---|---|---|
| 寫入過期 | expireAfterWrite() | 數(shù)據(jù)更新頻率低的場景 |
| 訪問過期 | expireAfterAccess() | 讀多寫少的場景 |
| 自適應過期 | expireAfter(Expiry) | 需要動態(tài)過期時間的場景 |
| 數(shù)量淘汰 | maximumSize() | 通用場景 |
| 權(quán)重淘汰 | maximumWeight() + weigher() | 緩存對象大小差異大的場景 |
| 異步刷新 | refreshAfterWrite() | 高并發(fā)讀取+后臺更新 |
| 弱/軟引用 | weakKeys()/softValues() | 內(nèi)存敏感型應用 |
注意事項
1.刷新 vs 過期:
- 刷新 (
refreshAfterWrite) 異步更新舊值,不阻塞請求 - 過期 (
expireAfterWrite) 會阻塞請求直到新值加載完成
2.權(quán)重計算:
- 確保
weigher計算快速(納秒級) - 權(quán)重總和不超過
maximumWeight
3.過期時間精度:
.scheduler(Scheduler.systemScheduler())
默認時間精度≈1秒,需要毫秒級精度可配置:
4.并發(fā)加載控制:
- 相同key并發(fā)請求時,只有一個線程執(zhí)行加載
- 可通過
executor()指定自定義線程池
到此這篇關于一文詳解Caffeine緩存庫的常用功能與使用指南的文章就介紹到這了,更多相關Caffeine緩存庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于spring boot實現(xiàn)一個全局異常處理器
在項目開發(fā)中,我們可以基于spring boot提供的切面特性,來很輕松的實現(xiàn)全局異常的處理,所以本文主要為大家介紹了如何基于spring boot實現(xiàn)一個全局異常處理器,有需要的可以參考下2023-09-09
Java Lambda表達式的方法引用和構(gòu)造器引用實例分析
這篇文章主要介紹了Java Lambda表達式的方法引用和構(gòu)造器引用,結(jié)合實例形式分析了Lambda表達式的方法引用和構(gòu)造器引用相關原理、用法及操作注意事項,需要的朋友可以參考下2019-09-09
Mybatis-Plus 條件構(gòu)造器 QueryWrapper 的基本用法
這篇文章主要介紹了Mybatis-Plus - 條件構(gòu)造器 QueryWrapper 的使用,通過實例代碼給大家介紹了查詢示例代碼及實現(xiàn)需求,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09

