常用緩存算法:
First in first out(FIFO)隊列:先進先出,最先進入的資料最先被淘汰,缺點:忽略資料通路頻率和通路次數。
Least recently used (LRU):最近最少使用算法,即:如果資料最近被通路過,那麼将來被通路的機率也更大。LRU使用一個連結清單來實作,新插入的資料會被添加到連結清單的頭部;當緩存命中後,就會再将資料移動到連結清單的頭部;如果連結清單滿的時候,就會将連結清單尾部的資料丢棄。
【命中率】如果存在熱點資料,LRU的效率很好,然而對于偶發性的、周期性的批量操作就會導緻LRU命中率急劇下降,緩存污染情況比較嚴重。
【複雜度】實作簡單。
【代價】通路資料命中時需要周遊連結清單,并找到命中的資料塊索引位置,然後再将資料移動到連結清單頭部。

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):監聽驅逐或無效操作的監聽器