緩存
對于經常訪問的數據,每次都從數據庫(硬盤)中獲取是比較慢,可以利用性能更高的存儲來提高系統響應速度,俗稱緩存 。合理使用緩存可以顯著降低數據庫的壓力、提高系統性能。
那么,什么樣的數據適合緩存呢?一般情況下就4個字“讀多寫少 ”,要頻繁查詢的、不怎么修改的。
具體來說:
- 高頻訪問的數據:如系統首頁、熱門推薦內容等。
- 計算成本較高的數據:如復雜查詢結果、大量數據的統計結果。
- 允許短時間延遲的數據:如不需要實時更新的排行榜、圖片列表等。
在我們的項目中,主頁是用戶高頻訪問的內容,調用的獲取圖片列表的接口也是高頻訪問的。而且即使數據更新存在一定延遲,也不會對用戶體驗造成明顯影響,因此非常適合緩存。
Redis分布式緩存
分布式緩存是指將緩存數據分布存儲在多臺服務器上,以便在高并發場景下提供更高的吞吐量和更好的容錯性。
Redis是實現分布式緩存的主流方案,也是后端開發必學的技能。主要是由于它具有下面幾個優勢:
- 高性能:基于內存操作,訪問速度極快。單節點 Redis的讀寫QPS可達10w次每秒!
- 豐富的數據結構:支持字符串、列表、集合、哈希、位圖等,適用于各種數據結構存儲。
- 分布式支持:可以通過RedisCluster構建高可用、高性能的分布式緩存,還提供哨兵集群機制提升可用性、提供分片集群機制提高可擴展性。
緩存設計
需要緩存首頁的圖片列表數據,也就是對 listPictureVOByPage接口進行緩存。首先按照緩存3要素"key、value、過期時間”進行設計。
- 緩存 key 設計
由于接口支持傳入不同的查詢條件,對應的數據不同,因此需要將查詢條件作為緩存key的一部分。
可以將查詢條件對象轉換為JSON字符串,但這個JSON會比較長,可以利用哈希算法(md5)來壓縮key。
此外,由于使用分布式緩存,可能由多個項目和業務共享,因此需要在key的開頭拼接前綴進行隔離。設計出的key如下:
yunpicture:listPictureVOByPage:${查詢條件key} - 緩存 value 設計
緩存從數據庫中查到的Page分頁對象,存儲為什么格式呢?這里有2種選擇:
- 為了可讀性,可以轉換為JSON結構的字符串
- 為了壓縮空間,可以存為二進制等其他結構
但是對應的 Redis 數據結構都是 string。
- 緩存過期時間設置
必須設置緩存過期時間! 根據實際業務場景和緩存空間的大小、數據的一致性的要求設置,合適即可,此處由于查詢條件較多、而且考慮到圖片會持續更新,設置為5~60分鐘即可。
Caffeine 本地緩存
當應用需要頻繁訪問某些數據時,可以將這些數據緩存到應用的內存中(比如JVM中);下次訪問時,直接從內存讀取,而不需要經過網絡或其他存儲系統。
相比于分布式緩存,本地緩存的速度更快,但是無法在多個服務器間共享數據、而且不方便擴容。
所以本地緩存的應用場景一般是:
- 數據訪問量有限的小型數據集
- 不需要服務器間共享數據的單機應用
- 高頻、低延遲的訪問場景(如用戶臨時會話信息、短期熱點數據)。
對于Java項目,Caffeine是主流的本地緩存技術,擁有極高的性能和豐富的功能。比如可以精確控制緩存數量和大小、支持緩存過期、支持多種緩存淘汰策略、支持異步操作、線程安全等。
由于本地緩存不需要引入額外的中間件,成本更低。因此如果只是要提升數據訪問性能,優先考慮本地緩存而不是分布式緩存。
緩存設計
本地緩存的設計和分布式緩存基本一致,不再贊述。但有2個區別:
- 本地緩存需要自己創建初始化緩存結構(可以簡單理解為要自己new一個HashMap)。
- 由于本地緩存本身就是服務器隔離的,而且占用服務器的內存,key可以更精簡一些,不用再添加項目前綴。
多級緩存
多級緩存是指結合本地緩存和分布式緩存的優點,在同一業務場景下構建兩級緩存系統,這樣可以兼顧本地緩存的高性能、以及分布式緩存的數據一致性和可靠性。
多級緩存的工作流程:
- 第一級(Caffeine本地緩存):優先從本地緩存中讀取數據。如果命中,則直接返回。
- 第二級(Redis分布式緩存):如果本地緩存未命中,則查詢Redis分布式緩存。如果Redis命中,則返回數據并更新本地緩存。
- 數據庫查詢:如果Redis也未命中,則查詢數據庫,并將結果寫入Redis和本地緩存。