🧑 博主簡介:CSDN博客專家,歷代文學網(PC端可以訪問:https://literature.sinhy.com/#/?__c=1000,移動端可微信小程序搜索“歷代文學”)總架構師,
15年
工作經驗,精通Java編程
,高并發設計
,Springboot和微服務
,熟悉Linux
,ESXI虛擬化
以及云原生Docker和K8s
,熱衷于探索科技的邊界,并將理論知識轉化為實際應用。保持對新技術的好奇心,樂于分享所學,希望通過我的實踐經歷和見解,啟發他人的創新思維。在這里,我希望能與志同道合的朋友交流探討,共同進步,一起在技術的世界里不斷學習成長。
技術合作請加本人wx(注明來自csdn):foreast_sea
Caffeine 緩存庫的常用功能使用介紹
文章目錄
- Caffeine 緩存庫的常用功能使用介紹
- 一、基礎緩存操作
- 二、自動加載緩存(推薦)
- 三、過期策略配置
- 1. 全局過期策略
- 2. 單Key過期(高級用法)
- 四、淘汰策略配置
- 五、刷新策略(優于純過期)
- 六、監聽器與統計
- 七、異步操作
- 八、最佳實踐配置模板
- 九、基于Caffeine實現的動態緩存
- 關鍵特性說明:
- 關鍵特性對比表
- 注意事項:
Caffeine
作為新一代高性能Java緩存庫,在并發場景下展現出卓越表現。它通過創新的W-TinyLFU
淘汰算法實現高達99%
的命中率,并采用無鎖設計使吞吐量較傳統方案提升5-10
倍。該庫提供靈活的緩存管理能力:支持基于時間(寫入/訪問過期)、數量或權重的淘汰策略;允許為單個Key設置專屬過期時間;獨創的異步刷新機制能在不阻塞請求的情況下更新數據。開發者可通過簡潔的鏈式API配置內存控制、加載邏輯和事件監聽,輕松構建高并發低延遲的智能緩存系統。其與Guava Cache
兼容的接口設計,更使遷移成本降至最低。
以下是 Caffeine 緩存庫的常用功能使用介紹,涵蓋基礎操作、過期策略、淘汰配置等核心功能:
一、基礎緩存操作
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;// 1. 創建緩存實例
Cache<String, Object> cache = Caffeine.newBuilder().build();// 2. 添加數據
cache.put("key1", "value1");// 3. 獲取數據(手動)
Object value = cache.getIfPresent("key1"); // 存在返回value,否則null// 4. 刪除數據
cache.invalidate("key1");
cache.invalidateAll(); // 清空緩存
二、自動加載緩存(推薦)
LoadingCache<String, Object> cache = Caffeine.newBuilder().build(key -> {// 緩存未命中時自動執行的加載邏輯return fetchFromDB(key); // 自定義數據庫加載方法});// 自動加載數據(緩存未命中時執行build中的邏輯)
Object value = cache.get("user_123");
三、過期策略配置
1. 全局過期策略
Cache<String, Object> cache = Caffeine.newBuilder()// 寫入后30分鐘過期.expireAfterWrite(30, TimeUnit.MINUTES)// 最后訪問后15分鐘過期.expireAfterAccess(15, TimeUnit.MINUTES)// 自定義過期策略(按需實現).expireAfter(new Expiry<String, Object>() {public long expireAfterCreate(String key, Object value, long currentTime) {return TimeUnit.MINUTES.toNanos(10); // 創建后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過期(高級用法)
// 創建支持變長過期的緩存
Cache<String, Object> cache = Caffeine.newBuilder().expireAfter(new Expiry<String, Object>() {// ...實現同上}).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()// 基于數量淘汰(最多1000個條目).maximumSize(1000)// 基于權重淘汰(需實現Weigher).maximumWeight(10_000).weigher((String key, Object value) -> {// 自定義權重計算邏輯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();
五、刷新策略(優于純過期)
LoadingCache<String, Object> cache = Caffeine.newBuilder()// 寫入后1分鐘可刷新(不阻塞讀取,異步刷新舊值).refreshAfterWrite(1, TimeUnit.MINUTES).build(key -> fetchFromDB(key));
六、監聽器與統計
Cache<String, Object> cache = Caffeine.newBuilder()// 移除監聽器.removalListener((String key, Object value, RemovalCause cause) -> {System.out.printf("Key %s was removed (%s)%n", key, cause);})// 啟用統計.recordStats().build();// 獲取統計信息
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));// 獲取數據(返回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)// 統計和監聽.recordStats().removalListener((key, value, cause) -> logRemoval(key, cause))// 自動加載.build(key -> fetchFromDB(key));
九、基于Caffeine實現的動態緩存
我們有時候需要這樣一種場景:當用戶請求某個key的時候,該緩存自動從數據庫去加載,就是注冊一個數據庫加載器(自己實現),當獲取不到該key時,自動走數據庫查詢,然后存入該key中。當往caffeine緩存中插入一個key后,如果緩存沒有,則自動存入,并自動同步到數據庫中,當刪除一個key,或key過期后,自動從數據庫同步刪除。
以下是簡單的實現流程:
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) // 最大緩存項// 注冊移除監聽器(用于刪除數據庫數據).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;});}// 獲取數據(自動加載)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;});}// 添加/更新數據(同步到數據庫)public void put(K key, V value) {// 先同步到數據庫dataSynchronizer.saveToDatabase(key, value);// 再更新緩存cache.put(key, value);}// 刪除數據(同步刪除數據庫)public void delete(K key) {// 先刪除數據庫數據dataSynchronizer.deleteFromDatabase(key);// 再使緩存失效cache.invalidate(key);}// 數據庫加載器接口public interface DataLoader<K, V> {V loadFromDatabase(K key);}// 數據庫同步器接口public interface DataSynchronizer<K, V> {void saveToDatabase(K key, V value);void deleteFromDatabase(K key);}
}
上述接口使用示例:
// 1. 實現數據庫操作接口
DynamicCache.DataLoader<String, User> loader = key -> jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", User.class, key);DynamicCache.DataSynchronizer<String, User> synchronizer = new DynamicCache.DataSynchronizer<>() {@Overridepublic void saveToDatabase(String key, User value) {jdbcTemplate.update("INSERT OR REPLACE INTO users (id, name) VALUES (?, ?)", key, value.getName());}@Overridepublic void deleteFromDatabase(String key) {jdbcTemplate.update("DELETE FROM users WHERE id=?", key);}
};// 2. 創建緩存實例
DynamicCache<String, User> userCache = new DynamicCache<>(loader, synchronizer);// 3. 使用緩存
// 自動加載(緩存未命中時從DB加載)
User user = userCache.get("user123"); // 添加/更新(同步到DB)
userCache.put("user456", new User("Alice"));// 刪除(同步刪除DB數據)
userCache.delete("user789");
關鍵特性說明:
-
自動加載:
- 當調用
get()
方法且緩存未命中時,自動通過DataLoader
從數據庫加載 - 加載成功后自動填充緩存
- 當調用
-
寫穿透:
put()
操作時:- 先通過
DataSynchronizer
保存到數據庫 - 再更新緩存
- 先通過
- 保證數據庫與緩存的數據一致性
-
刪除同步:
delete()
操作時:- 先刪除數據庫數據
- 再使緩存失效
- 緩存過期/淘汰時:
- 通過 RemovalListener 自動觸發數據庫刪除
-
緩存配置:
- 可自定義過期時間(
expireAfterWrite
) - 可設置最大容量(
maximumSize
) - 支持其他Caffeine特性(刷新策略、弱引用等)
- 可自定義過期時間(
關鍵特性對比表
功能 | 配置方法 | 適用場景 |
---|---|---|
寫入過期 | expireAfterWrite() | 數據更新頻率低的場景 |
訪問過期 | expireAfterAccess() | 讀多寫少的場景 |
自適應過期 | expireAfter(Expiry) | 需要動態過期時間的場景 |
數量淘汰 | maximumSize() | 通用場景 |
權重淘汰 | maximumWeight() + weigher() | 緩存對象大小差異大的場景 |
異步刷新 | refreshAfterWrite() | 高并發讀取+后臺更新 |
弱/軟引用 | weakKeys() /softValues() | 內存敏感型應用 |
注意事項:
-
刷新 vs 過期:
- 刷新 (
refreshAfterWrite
) 異步更新舊值,不阻塞請求 - 過期 (
expireAfterWrite
) 會阻塞請求直到新值加載完成
- 刷新 (
-
權重計算:
- 確保
weigher
計算快速(納秒級) - 權重總和不超過
maximumWeight
- 確保
-
過期時間精度:
- 默認時間精度≈1秒,需要毫秒級精度可配置:
.scheduler(Scheduler.systemScheduler())
-
并發加載控制:
- 相同key并發請求時,只有一個線程執行加載
- 可通過
executor()
指定自定義線程池