當前位置: 妍妍網 > 碼農

本地緩存怎麽選型?

2024-03-26碼農

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利

全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ ,新計畫正在醞釀中。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了239小節,累計38w+字,講解圖:1645張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

1、簡介

最近在使用系統的時候,發現盡管套用已經使用了 redis 緩存提高查詢效率,但是仍然有進一步最佳化的空間,於是想到了比分布式緩存效能更好的本地緩存,因此對領域內常用的本地緩存進行了一番調研,有早期的 Guava 緩存、在 Guava 上進一步傳承的 Caffine 以及自稱在 Java 中使用最廣泛的 EhCache,那麽我們該怎麽選擇適合自己套用的緩存呢,下面會簡單介紹,並將以上緩存進行一個對比,希望幫助大家選擇最適合自己系統的本地緩存。

2、Guava 緩存簡介

Guava cache 是 Google 開發的 Guava 工具包中一套完善的 JVM 本地緩存框架,底層實作的數據結構類似於 ConcurrentHashMap,但是進行了更多的能力拓展,包括緩存過期時間設定、緩存容量設定、多種淘汰策略、緩存監控等,下面簡單介紹下這些功能及其使用方式。

2.1、緩存過期時間設定

Guava 的過期時間設定有基於建立時間和最後一次存取時間兩種策略.

(1) 基於建立時間

透過對比緩存記錄的插入時間來判斷,比如設定過期時間為 5 分鐘,不管中間有沒有存取,到時過期。

public Cache<String, String> createCache() {
return CacheBuilder.newBuilder()
.expireAfterWrite(5L, TimeUnit.MINUTES)
.build();
}

(2) 基於過期時間

透過對比最近最後一次的存取時間,比如設定 5 分鐘,每次存取之後都會重新整理過期時間為 5 分鐘,只有持續 5 分鐘沒有被存取到才會過期。

public Cache<String, String> createCache() {
return CacheBuilder.newBuilder()
.expireAfterAccess(5L, TimeUnit.MINUTES)
.build();
}

2.2、緩存容量和淘汰策略設定

Guava cache 是記憶體型緩存,有記憶體溢位風險,因此需要設定緩存的最大儲存上限,透過緩存的條數或每條緩存的權重來判斷是否達到了設定閾值,當緩存的數據量達到設定閾值之後,Guava cache 支持使用 FIFO 和 LRU 的策略對緩存記錄采取淘汰的措施。

(1)限制緩存記錄條數

public Cache<String, User> createCache() {
return CacheBuilder.newBuilder()
.maximumSize(100L)
.build();
}

(2)限制緩存記錄權重

public Cache<String, User> createCache() {
return CacheBuilder.newBuilder()
.maximumWeight(100L)
.weigher((key, value) -> (int) Math.ceil(instrumentation.getObjectSize(value) / 1024L))
.build();
}

使用限制緩存記錄權重時要先計算 weight 的 value 物件的字節數,每 1kb 字節作為一個權重,對比限制緩存記錄,我們就能將緩存的總占用限制在 100kb 左右。

2.3 緩存監控

緩存記錄的載入和命中情況是評價緩存處理能力的重要指標,Guava cache 提供了 stat 統計日誌對這兩個指標進行了統計,我們只需要在建立緩存容器的時候加上 recordStats 就可以開啟統計。

public Cache<String, User> createCache() {
return CacheBuilder.newBuilder()
.recordStats()
.build();
}

2.4 Guava cache 的優劣勢和適用場景

優劣勢:Guava cache 透過記憶體處理數據,具有減少 IO 請求,讀寫效能快的優勢,但是受記憶體容量限制,只能處理少量數據的讀寫,還有可能對本機記憶體造成壓力,並且在分布式部署中,會存在不同機器節點數據不一致的情況,即緩存漂移。

適用場景:讀多寫少,對數據一致性要求不高的場景。

3、Caffeine 簡介

Caffeine 同樣是 Google 開發的,是在 Guava cache 的基礎上改良而來的,底層設計思路、功能和使用方式與 Guava 非常類似,但是各方面的效能都要遠遠超過前者,可以看做是 Guava cache 的升級版,因此,之前使用過 Guava cache,也能夠很快的上手 Caffeine,下面是 Caffeine 和 Guava cache 的緩存建立對比,基本可以無門檻過渡。

public Cache<String, String> createCache() {
return Caffeine.newBuilder()
.initialCapacity(1000)
.maximumSize(100L)
.expireAfterWrite(5L, TimeUnit.MINUTES)
.recordStats()
.build();
}
public Cache<String, String> createCache() {
return CacheBuilder.newBuilder()
.initialCapacity(1000)
.maximumSize(100L)
.expireAfterWrite(5L, TimeUnit.MINUTES)
.recordStats()
.build();
}

那麽 Caffeine 底層又做了哪些最佳化,才能讓其效能高於 Guava cache 呢?主要包含以下三點:

3.1、對比 Guava cache 的效能主要最佳化項

(1)異步策略

Guava cache 在讀操作中可能會觸發淘汰數據的清理操作,雖然自身也做了一些最佳化來減少讀的時候的清理操作,但是一旦觸發,就會降低查詢效率,對緩存效能產生影響。而在 Caffeine 支持異步操作,采用異步處理的策略,查詢請求在觸發淘汰數據的清理操作後,會將清理數據的任務添加到獨立的執行緒池中進行異步操作,不會阻塞查詢請求,提高了查詢效能。

圖片

(2)ConcurrentHashMap 最佳化

Caffeine 底層都是透過 ConcurrentHashMap 來進行數據的儲存,因此隨著 Java8 中對 ConcurrentHashMap 的調整,陣列 + 連結串列的結構升級為陣列 + 連結串列 + 紅黑樹的結構以及分段鎖升級為 syschronized+CAS,降低了鎖的粒度,減少了鎖的競爭,這兩個最佳化顯著提高了 Caffeine 在讀多寫少場景下的查詢效能。

(3)新型淘汰演算法 W-TinyLFU

傳統的淘汰演算法,如 LRU、LFU、FIFO,在實際的緩存場景中都存在一些弊端,如 FIFO 演算法,如果緩存使用的頻率較高,那麽緩存數據會一直處在進進出出的狀態,間接影響到緩存命中率。LRU 演算法,在批次清除快取數據的場景下,可能會將其他緩存數據淘汰掉,從而帶來緩存擊穿的風險。LFU 演算法,需要保存緩存記錄的存取次數,帶來記憶體空間的損耗。

因此,Caffeine 引入了 W-TinyLFU 演算法,由視窗緩存、過濾器、主緩存組成。緩存數據剛進入時會停留在視窗緩存中,這個部份只占總緩存的 1%,當被擠出視窗緩存時,會在過濾器匯總和主緩存中淘汰的數據進行比較,如果頻率更高,則進入主緩存,否則就被淘汰,主緩存被分為淘汰段和保護段,兩段都是 LRU 演算法,第一次被存取的元素會進入淘汰段,第二次被存取會進入保護段,保護段中被淘汰的元素會進入淘汰段,這種演算法實作了高命中率和低記憶體占用。更詳細的解釋可以參考論文:https://arxiv.org/pdf/1512.00727.pdf

圖片

3.2、Caffeine 的優劣勢和適用場景

優勢:對比 Guava cache 有更高的緩存效能,劣勢:仍然存在緩存漂移的問題;JDK 版本過低無法使用

適用場景:1、適用場景:讀多寫少,對數據一致性要求不高的場景;2、純記憶體緩存,JDK8 及更高版本中,追求比 Guava cache 更高的效能。

4、Ehcache 簡介

Guava cache 和 Caffeine 都是 JVM 緩存,會受到記憶體大小的制約,最新的 Ehcache 采用堆內緩存 + 堆外緩存 + 磁盤的方式,打破了這一制約。堆內緩存就是被 JVM 管理的那一部份緩存,而堆外緩存,就是在記憶體中另外在開辟一塊不被 JVM 管理的部份。堆外緩存這部份既可以享受記憶體的高速讀寫能力,而且又避免的 JVM 頻繁的 GC,缺點是需要自行清理數據。

圖片

下面是 Ehcache 緩存的建立,指定了堆內、堆外緩存和磁盤緩存的大小。

ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(20, MemoryUnit.MB)
.offheap(10, MemoryUnit.MB)
.disk(5, MemoryUnit.GB);

為了解決緩存漂移的問題,Ehcache 支持透過集群的方式,實作了分布式節點之間的數據互通。關於 Ehcache 的集群策略,後續文章再詳細闡述。

5、不同本地緩存對比

框架 命中率 速度 回收演算法 使用難度 集群 適用場景
Guava cache 第三 LRU、LFU、FIFO 不支持 讀多寫少,允許少量緩存偏移
Caffeine 第一 W-TinyLFU 不支持 讀多寫少,允許少量緩存偏移,能用 Caffeine 就別用 Guava cache
Ehcache 第二 LRU、LFU、FIFO 支持 分布式系統中對數據一致性要求高

好書推薦

掌握AI大語言模型,開啟智慧套用新時代!

學會構建高品質的提示指令, 掌握利用人工智慧工具的藝術, 成為與AI交流的高手

讓Excel 365 & Excel 2021快速轉化為生產力,創造價值!

【Excel 2019函式與公式套用大全】全新升級版, Excel Home多位微軟全球MVP專家打造, 精選Excel Home海量案例, 披露Excel專家多年研究成果, 讓你分分鐘搞定海量數據運算

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 贈書福利

全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ ,新計畫正在醞釀中。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了239小節,累計38w+字,講解圖:1645張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


1. 

2. 

3. 

4. 

最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

「在看」支持小哈呀,謝謝啦