天天看點

Caffeine 本地緩存架構原理及用法總結

常用緩存算法:

First in first out(FIFO)隊列:先進先出,最先進入的資料最先被淘汰,缺點:忽略資料通路頻率和通路次數。

Least recently used (LRU):最近最少使用算法,即:如果資料最近被通路過,那麼将來被通路的機率也更大。LRU使用一個連結清單來實作,新插入的資料會被添加到連結清單的頭部;當緩存命中後,就會再将資料移動到連結清單的頭部;如果連結清單滿的時候,就會将連結清單尾部的資料丢棄。

【命中率】如果存在熱點資料,LRU的效率很好,然而對于偶發性的、周期性的批量操作就會導緻LRU命中率急劇下降,緩存污染情況比較嚴重。

【複雜度】實作簡單。

【代價】通路資料命中時需要周遊連結清單,并找到命中的資料塊索引位置,然後再将資料移動到連結清單頭部。

Caffeine 本地緩存架構原理及用法總結

Least frequently used (LFU):最近經常使用算法,即:如果資料過去被通路多次,那麼将來被通路的頻率也更高。

Caffeine緩存算法:它結合了LRU和LFU兩種算法來管理緩存。

Caffeine資料結構:

Caffeine采用的是readBuffer和writeBuffer。它使用一個Long數組來存儲key的通路頻率。Long的64位被分為4個段,每個段占16位。首先,根據key算出hash值,使用hash值來計算處于4段中哪個段;其次,計算出4個段的具體位置,并設定目前位置的頻率;最終以這4個頻率值的最小值來代表該key的通路頻率。

ringbuffer預設為16,其數組大小是256,假設引用大小為4位元組(ringbuffer中存儲的是引用),緩存行大小為64。是以這裡每個緩存行隻存一個資料,是以cas操作追加16,即數組中每16個元素隻有一個有效存儲,以空間換時間。

高性能讀寫操作:

借鑒資料庫的WAL思想,讀寫操作後,将操作記錄(判斷資料是否過期;統計頻率;記錄讀寫統計命中率等)記載到緩沖區,再異步處理,提高了性能。

WAL(Write-Ahead Logging)的核心思想是:在資料寫入到資料庫之前,先寫入到日志.再将日志記錄變更到存儲器中。

Caffeine緩存的三種回收政策

基于大小(size-based):maximumSize

基于時間(time-based):

expireAfterAccess 最後一次通路過期計時
     expireAfterWrite  最後一次寫入時過期計時           

基于引用(reference-based):僅對軟引用、弱引用過期處理

使用方法:

// 初始化Caffeine對象
@Bean
public Cache<String, Object> caffeineCacheInstance() {
Cache<String, Object> caffeineCache = Caffeine.newBuilder().initialCapacity(initialCapacity)
// 設定永不過期,預設300年
.expireAfterAccess(Long.MAX_VALUE, TimeUnit.DAYS)
.expireAfterWrite(Long.MAX_VALUE, TimeUnit.DAYS)
// 設定移除監聽器
.removalListener(new CacheRemoveListener())
// 不限容量
//.maximumSize(maximumSize)
.build();
return caffeineCache;
}
// 增加處理緩存移除監聽器類CacheRemoveListener
public class CacheRemoveListener implements RemovalListener<Object, Object> {
@Override
public void onRemoval(@Nullable Object key, @Nullable Object value, @Null RemovalCause cause) {
// 緩存被回收或過期時處理
if(cause.equals(RemovalCause.COLLECTED) || cause.equals(RemovalCause.EXPIRED)) {
// 當對象被回收或過期時需要處理
return;
}
// 緩存更新時處理
if(cause.equals(RemovalCause.REPLACED)) {
logger.warn("Caffeine " + cause + " OK, key-value, " + key + ":" + value);
}
}
}
           

用法:在具體的@Service類中引用

@Autowired

private Cache<String, Object> caffeineCacheInstance;

存放:caffeineCacheInstance.put(key, value);

讀取:Object value = caffeineCacheInstance.getIfPresent(key);

使用時需要注意點:

(1)初始化本地緩存資料

(2)容量評估,不要超出記憶體容量

(3)緩存命中率

(4)垃圾回收

(5)性能測試

相關名詞解釋:

驅逐(eviction):由于滿足了某種驅逐政策,背景自動進行的删除操作

無效(invalidation):表示由調用方手動删除緩存

移除(removal):監聽驅逐或無效操作的監聽器