淺談LocalCache | 京東云技術團隊
1、什么是LocalCache?
本地緩存是一種將數據存儲在應用程序內存中的機制,用于提高數據訪問的性能和響應速度。它通過在內存中維護一個鍵值對的存儲結構,允許應用程序快速檢索和訪問數據,而無需每次都從慢速的數據源(如數據庫或網絡)獲取數據。
2、LocalCache優缺點
1)優點
LocalCache將數據存儲在內存中,因此可以快速訪問緩存數據,提高應用程序的性能。LocalCache提供了一些配置選項,如最大容量、過期時間等,可以根據需求進行調整。這使得開發人員能夠靈活地控制緩存的行為,以適應不同的業務場景。2)缺點
LocalCache是基于內存的,因此容量是有限的。如果緩存數據量過大,可能會導致內存消耗過高,影響系統的穩定性。LocalCache存儲在單個應用程序中,如果應用程序崩潰或重啟,緩存數據將丟失。這可能會導致緩存冷啟動和性能下降。3、LocalCache應用場景
LocalCache適用于許多不同的使用場景,特別是以下幾種情況:
LocalCache中可以大大提高訪問速度。由于數據存儲在內存中,相比于從磁盤或網絡中讀取數據,從本地緩存中獲取數據的速度更快。LocalCache提供了一些配置選項,如最大容量、過期時間等,可以根據需求進行調整。這使得開發人員能夠靈活地控制緩存的行為,以適應不同的業務場景。4、LocalCache實際使用場景
LocalCache來緩存查詢結果,以減輕后端數據庫的負載并提高查詢性能。當應用程序發出相同的查詢請求時,中間件可以先檢查LocalCache中是否有相應的緩存結果,如果有則直接返回緩存結果,否則再向后端數據庫發起查詢請求。LocalCache可以用來緩存消息,以提高消息的處理速度和響應能力。當消費者需要處理消息時,它可以先在LocalCache中查找是否有相應的消息緩存,如果有則直接消費緩存消息,否則再從消息隊列中獲取消息。LocalCache可以用于緩存靜態資源(如JavaScript、CSS、圖像等)或動態數據,以提高應用的加載速度和用戶體驗。例如,前端應用可以先檢查LocalCache中是否有相應的資源緩存,如果有則直接使用緩存資源,否則再從服務器獲取資源并將其緩存到LocalCache中。LocalCache可以在服務端應用中使用,用于緩存計算結果、數據庫查詢結果等,以提高性能和減少對外部資源的依賴。例如,當服務端應用需要進行頻繁的計算或查詢時,它可以先檢查LocalCache中是否有相應的緩存結果,如果有則直接返回緩存結果,否則再進行計算或查詢,并將結果緩存到LocalCache中。LocalCache實現,以方便開發人員在應用中使用。這些框架通常提供了豐富的配置選項和高性能的緩存實現,可以根據應用需求進行定制。例如,Guava、Caffeine和Ehcache等是常見的Java開源框架,它們提供了LocalCache的實現,可以用于緩存計算結果、數據庫查詢結果等。5、LocalCache在Guava實踐
Guava cache 繼承了 ConcurrentHashMap 的思路,使用多個 segments 方式的細粒度鎖,在保證線程安全的同時,支持高并發場景需求。
1、CacheBuilder 緩存構建器。構建緩存的入口,指定緩存配置參數并初始化本地緩存。采用 Builder 設計模式提供了設置好各種參數的緩存對象。
CacheBuilder<Object,Object> cacheBuilder =CacheBuilder.newBuilder();
2、LocalCache 數據結構。緩存核心類 LocalCache 數據結構與 ConcurrentHashMap 很相似,由多個 segment 組成,且各 segment 相對獨立,互不影響,所以能支持并行操作,每個 segment 由一個 table 和若干隊列組成。緩存數據存儲在 table 中,其類型為AtomicReferenceArray,具體結構圖及代碼解釋如下。

#Guava中LocalCache聲明segments變量
final LocalCache.Segment<K, V>[] segments;
#Guava中初始化segments
this.segments = this.newSegmentArray(segmentCount);
final LocalCache.Segment<K, V>[] newSegmentArray(int ssize) {
return new LocalCache.Segment[ssize];
}
#獲取Segment
Segment<K, V> segmentFor(int hash) {
return segments[(hash >>> segmentShift) & segmentMask];
}
#Guava中Segment聲明table變量用于存儲數據
volatile AtomicReferenceArray<LocalCache.ReferenceEntry<K, V>> table;
V put(K key, int hash, V value, boolean onlyIfAbsent) {
#保證線程安全
lock();
#獲取數據
AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table;
int index = hash & (table.length() - 1);
ReferenceEntry<K, V> first = table.get(index);
for (ReferenceEntry<K, V> e = first; e != null; e = e.getNext()) {
if (e.getHash() == hash
&& entryKey != null
&& map.keyEquivalence.equivalent(key, entryKey)) {
}
}
}
3、在Guava中,CacheBuilder提供了一系列方法,用于指定緩存的大小、過期策略、并發級別等屬性。
maximumSize(long size):指定緩存的最大容量,當緩存達到最大容量時,根據緩存策略淘汰部分緩存項。expireAfterWrite(Duration duration):指定緩存項的寫入后過期時間,過期后的緩存項將被自動移除。expireAfterAccess(Duration duration):指定緩存項的最后一次訪問后過期時間,過期后的緩存項將被自動移除。concurrencyLevel(int level):指定并發級別,即同時可以進行緩存操作的線程數。Cache<Object,Object> cache = cacheBuilder.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.concurrencyLevel(4)
.build();
2. 使用緩存:通過Cache對象的方法來進行緩存的讀取、寫入和移除操作。例如:
get(Object key):根據鍵獲取緩存項的值。

put(Object key, Object value):向緩存中添加或更新一個緩存項。

invalidate(Object key):移除指定鍵的緩存項。

3. 其他功能:Guava的LocalCache還提供了其他功能,如統計信息、監聽器、加載器等,用于監控緩存的狀態、處理緩存未命中的情況等。
CacheStats stats = cache.stats();
cache.asMap().forEach((key, value)->System.out.println(key +": "+ value));
cache.cleanUp();
總結來說,Guava的LocalCache使用CacheBuilder構建和配置緩存,通過Cache對象進行緩存的讀取、寫入和移除操作。開發人員可以根據自己的需求使用相應的方法和功能來定制和管理緩存。
6、后記
設計一個可用的 Cache 絕對不是一個普通的 Map 這么簡單,這里小結一下關于 Guava Cache 的知識。
回歸LocalCache 的源頭,我是希望可以了解 設計一個緩存要考慮什么?,局部性原理 是一個系統性能提升的最直接的方式(編程上,硬件上當然也可以),緩存的出現就是根據 局部性原理 所設計的。

緩存作為存儲金字塔的一部分,一定需要考慮以下幾個問題:
在設計何時加載的問題上,Guava Cache 提供了一個 Loader 接口,讓用戶可以自定義加載過程,在由 Cache 在找不到對象的時候主動調用 Loader 去加載,還通過一個巧妙的方法,既保證了 Loader 的只運行一次,還能保證鎖粒度極小,保證并發加載時,安全且高性能。
2. 何時失效
失效處理上,Guava Cache 提供了基于容量、有限時間(讀有限時、寫有限時)等失效策略,在官方文檔上也寫明,在基于限時的情況下,并不是使用一個線程去單獨清理過期 K-V,而是把這個清理工作,均攤到每次訪問中。假如需要定時清理,也可以調用 CleanUp 方法,定時調用就可以了。
3. 如何保持熱點數據有效性
在 Cache 容量有限時, LRU 算法是一個通用的解決方案,在源碼中,Guava Cache 并不是嚴格地保證全局 LRU 的,只是針對一個 Segment 實現 LRU 算法。這個前提是 Segment 對用戶來說是隨機的,所以全局的 LRU 算法和單個 Segment 的算法是基本一致的。
4. 寫回策略
在 Guava Cache 里,并沒有實現任何的寫回策略。原因在于,Guava Cache 是一個本地緩存,直接修改對象的數據,Cache 的數據就已經是最新的了,所以在數據能夠寫入 DB 后,數據就已經完成一致了。
參考文獻:Google Guava Cache 全解析
作者:京東科技 游斌平
來源:京東云開發者社區 轉載請注明來源
浙公網安備 33010602011771號