文章插圖

文章插圖
一、前言
在項目開發中,為提升系統性能,減少 IO 開銷,本地緩存是必不可少的 。最常見的本地緩存是 Guava 和 Caffeine,本篇文章將為大家介紹 Caffeine 。
Caffeine 是基于 Google Guava Cache 設計經驗改進的結果,相較于 Guava 在性能和命中率上更具有效率,你可以認為其是 Guava Plus 。
毋庸置疑的,你應該盡快將你的本地緩存從 Guava 遷移至 Caffeine,本文將重點和 Guava 對比二者性能占據,給出本地緩存的最佳實踐,以及遷移策略 。
二、PK Guava2.1 功能
2.2 性能
Guava 中其讀寫操作夾雜著過期時間的處理,也就是你在一次 put 操作中有可能會做淘汰操作,所以其讀寫性能會受到一定影響 。
Caffeine 在讀寫操作方面完爆 Guava,主要是因為 Caffeine 對這些事件的操作是異步的,將事件提交至隊列(使用 Disruptor RingBuffer),然后會通過默認的 ForkJoinPool.commonPool(),或自己配置的線程池,進行取隊列操作,然后再進行后續的淘汰、過期操作 。
以下性能對比來自 Caffeine 官方提供數據:
(1)在此基準測試中,從配置了最大大小的緩存中,8 個線程并發讀:
image.png
(3)在此基準測試中,從配置了最大大小的緩存中,8 個線程并發寫:
2.3 命中率
緩存的淘汰策略是為了預測哪些數據在短期內最可能被再次用到,從而提升緩存的命中率 。Guava 使用 S-LRU 分段的最近最少未使用算法,Caffeine 采用了一種結合 LRU、LFU 優點的算法:W-TinyLFU,其特點是:高命中率、低內存占用 。
2.3.1 LRU
Least Recently Used:如果數據最近被訪問過,將來被訪問的概率也更高 。每次訪問就把這個元素放到隊列的頭部,隊列滿了就淘汰隊列尾部的數據,即淘汰最長時間沒有被訪問的 。
需要維護每個數據項的訪問頻率信息,每次訪問都需要更新,這個開銷是非常大的 。
其缺點是,如果某一時刻大量數據到來,很容易將熱點數據擠出緩存,留下來的很可能是只訪問一次,今后不會再訪問的或頻率極低的數據 。比如外賣中午時候訪問量突增、微博爆出某明星糗事就是一個突發性熱點事件 。當事件結束后,可能沒有啥訪問量了,但是由于其極高的訪問頻率,導致其在未來很長一段時間內都不會被淘汰掉 。
2.3.2 LFU
Least Frequently Used:如果數據最近被訪問過,那么將來被訪問的概率也更高 。也就是淘汰一定時間內被訪問次數最少的數據(時間局部性原理) 。
需要用 Queue 來保存訪問記錄,可以用 LinkedHashMap 來簡單實現一個基于 LRU 算法的緩存 。
其優點是,避免了 LRU 的缺點,因為根據頻率淘汰,不會出現大量進來的擠壓掉 老的,如果在數據的訪問的模式不隨時間變化時候,LFU 能夠提供絕佳的命中率 。
其缺點是,偶發性的、周期性的批量操作會導致LRU命中率急劇下降,緩存污染情況比較嚴重 。
2.3.3 TinyLFU
TinyLFU 顧名思義,輕量級LFU,相比于 LFU 算法用更小的內存空間來記錄訪問頻率 。
TinyLFU 維護了近期訪問記錄的頻率信息,不同于傳統的 LFU 維護整個生命周期的訪問記錄,所以他可以很好地應對突發性的熱點事件(超過一定時間,這些記錄不再被維護) 。這些訪問記錄會作為一個過濾器,當新加入的記錄(New Item)訪問頻率高于將被淘汰的緩存記錄(Cache Victim)時才會被替換 。流程如下:
tiny-lfu-arch
盡管維護的是近期的訪問記錄,但仍然是非常昂貴的,TinyLFU 通過 Count-Min Sketch 算法來記錄頻率信息,它占用空間小且誤報率低,關于 Count-Min Sketch 算法可以參考論文:pproximating Data with the Count-Min Data Structure
2.3.4 W-TinyLFU
W-TinyLFU 是 Caffeine 提出的一種全新算法,它可以解決頻率統計不準確以及訪問頻率衰減的問題 。這個方法讓我們從空間、效率、以及適配舉證的長寬引起的哈希碰撞的錯誤率上做均衡 。
下圖是一個運行了 ERP 應用的數據庫服務中各種算法的命中率,實驗數據來源于 ARC 算法作者,更多場景的性能測試參見官網:
database
W-TinyLFU 算法是對 TinyLFU算法的優化,能夠很好地解決一些稀疏的突發訪問元素 。在一些數目很少但突發訪問量很大的場景下,TinyLFU將無法保存這類元素,因為它們無法在短時間內積累到足夠高的頻率,從而被過濾器過濾掉 。W-TinyLFU 將新記錄暫時放入 Window Cache 里面,只有通過 TinLFU 考察才能進入 Main Cache 。大致流程如下圖:
W-TinyLFU
三、最佳實踐3.1 實踐1
配置方式:設置 maxSize、refreshAfterWrite,不設置 expireAfterWrite
存在問題:get 緩存間隔超過 refreshAfterWrite 后,觸發緩存異步刷新,此時會獲取緩存中的舊值
適用場景:緩存數據量大,限制緩存占用的內存容量緩存值會變,需要刷新緩存可以接受任何時間緩存中存在舊數據
3.2 實踐2
配置方式:設置 maxSize、expireAfterWrite,不設置 refreshAfterWrite
存在問題:get 緩存間隔超過 expireAfterWrite 后,針對該 key,獲取到鎖的線程會同步執行 load,其他未獲得鎖的線程會阻塞等待,獲取鎖線程執行延時過長會導致其他線程阻塞時間過長
適用場景:緩存數據量大,限制緩存占用的內存容量緩存值會變,需要刷新緩存不可以接受緩存中存在舊數據同步加載數據延遲?。ㄊ褂?redis 等)
3.3 實踐3
配置方式:設置 maxSize,不設置 refreshAfterWrite、expireAfterWrite,定時任務異步刷新數據
存在問題:需要手動定時任務異步刷新緩存
適用場景:緩存數據量大,限制緩存占用的內存容量緩存值會變,需要刷新緩存不可以接受緩存中存在舊數據同步加載數據延遲可能會很大
g
設置 maxSize,不設置 refreshAfterWrite、expireAfterWrite,定時任務異步刷新數據
3.4 實踐4
配置方式:設置 maxSize、refreshAfterWrite、expireAfterWrite,refreshAfterWrite < expireAfterWrite
存在問題:get 緩存間隔在 refreshAfterWrite 和 expireAfterWrite 之間,觸發緩存異步刷新,此時會獲取緩存中的舊值get 緩存間隔大于 expireAfterWrite,針對該 key,獲取到鎖的線程會同步執行 load,其他未獲得鎖的線程會阻塞等待,獲取鎖線程執行延時過長會導致其他線程阻塞時間過長
適用場景:緩存數據量大,限制緩存占用的內存容量緩存值會變,需要刷新緩存可以接受有限時間緩存中存在舊數據同步加載數據延遲?。ㄊ褂?redis 等)
四、遷移指南
4.1 切換至 Caffeine
在 pom 文件中引入 Caffeine 依賴:
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId></dependency>Caffeine 兼容 Guava API,從 Guava 切換到 Caffeine,僅需要把 CacheBuilder.newBuilder()改成 Caffeine.newBuilder() 即可 。4.2 Get Exception
需要注意的是,在使用 Guava 的 get()方法時,當緩存的 load()方法返回 null 時,會拋出 ExecutionException 。切換到 Caffeine 后,get()方法不會拋出異常,但允許返回為 null 。
Guava 還提供了一個getUnchecked()方法,它不需要我們顯示的去捕捉異常,但是一旦 load()方法返回 null時,就會拋出 UncheckedExecutionException 。切換到 Caffeine 后,不再提供 getUnchecked()方法,因此需要做好判空處理 。
- 英文情話大全浪漫情話 用英語表白的情話短篇撩妹
- 猜猜我有多愛你 繪本 猜猜我有多愛你 英文
- 磁盤碎片清理手機軟件 手機磁盤碎片整理app
- 白灰墻面臟了怎么清理變白
- openwrt英文設置教程 openwrt路由器設置中文
- 清理垃圾軟件最好用的是哪一個?電腦 哪一種清理垃圾的軟件最好
- 電腦清理c盤垃圾運行命令代碼 電腦清理c盤清理代碼
- 手機最好的清理軟件是哪個 手機最好的清理軟件?
- 撩漢100句英文版,nothing,can,stop,love
- 什么軟件免費清理c盤 免費清理電腦c盤的軟件
