轉載自:devillyd2018的部落格_CSDN部落格
随着網際網路的發展,使用者規模和資料規模越來越大,對系統的性能提出了更高的要求,緩存就是其中一個非常關鍵的元件,從簡單的商品秒殺,到全民投入的雙十一,我們都能見到它的身影。
分布式緩存首先也是緩存,一種性能很好但是相對稀缺的資源,和我們在課本上學習的CPU緩存原理基本相同,CPU是用性能更好的靜态RAM來為性能一般的DRAM加速,分布式緩存則是通過記憶體或者其他高速存儲來加速,但是由于用到了分布式環境中,涉及到并發和網絡的問題,是以會更加複雜一些,但是有很多方面的共性,比如緩存淘汰政策。計算機行業有一句鼎鼎大名的格言就指出了緩存失效的複雜性。
There are only two hard things in Computer Science: cache invalidation and naming things (計算科學中最難的兩件事是命名和緩存失效)
– Phil Karlton
本文包括四個部分,分布式緩存的更新模式、失效機制、淘汰政策和常見問題及解決方案,重點是圍繞緩存的通用原理和實作來說明,不針對某個具體的系統,算法部分主要采用僞代碼說明。
緩存的更新模式
Cache Aside模式
- 讀取失效:cache資料沒有命中,查詢DB,成功後把資料寫入緩存
- 讀取命中:讀取cache資料
- 更新:把資料更新到DB,失效緩存
圖示

為什麼更新不直接寫緩存?
為了降低并發情況下的資料不一緻發生機率(cache aside無法完全避免資料不一緻,隻能降低發生的機率,如果需要資料強一直可以考慮使用分布式鎖),如下圖所示
這種情況下如果改為失效的話資料不一緻的情況能夠避免
Read/Write Through模式
緩存代理了DB讀取、寫入的邏輯,可以把緩存看成唯一的存儲。
Write Behind Caching(Write Back)模式
這種模式下所有的操作都走緩存,緩存裡的資料再通過異步的方式同步到資料庫裡面。是以系統的寫性能能夠大大提升了。
緩存失效政策
一般而言,緩存系統中都會對緩存的對象設定一個逾時時間,避免浪費相對比較稀缺的緩存資源。對于緩存時間的處理有兩種,分别是主動失效和被動失效。
主動失效
主動失效是指系統有一個主動檢查緩存是否失效的機制,比如通過定時任務或者單獨的線程不斷的去檢查緩存隊列中的對象是否失效,如果失效就把他們清除掉,避免浪費。主動失效的好處是能夠避免記憶體的浪費,但是會占用額外的CPU時間。
被動失效
被動失效是通過通路緩存對象的時候才去檢查緩存對象是否失效,這樣的好處是系統占用的CPU時間更少,但是風險是長期不被通路的緩存對象不會被系統清除。
緩存淘汰政策
緩存淘汰,又稱為緩存逐出(cache replacement algorithms或者cache replacement policies),是指在存儲空間不足的情況下,緩存系統主動釋放一些緩存對象擷取更多的存儲空間。
對于大部分記憶體型的分布式緩存(非持久化),淘汰政策優先于失效政策,一旦空間不足,緩存對象即使沒有過期也會被釋放。這裡隻是簡單介紹一下,相關的資料都很多,一般LRU用的比較多,可以重點了解一下。
FIFO
先進先出(First In First Out)是一種簡單的淘汰政策,緩存對象以隊列的形式存在,如果空間不足,就釋放隊列頭部的(先緩存)對象。一般用連結清單實作。
LRU
最近最久未使用(Least Recently Used),這種政策是根據通路的時間先後來進行淘汰的,如果空間不足,會釋放最久沒有通路的對象(上次通路時間最早的對象)。比較常見的是通過優先隊列來實作。
LFU
最近最少使用(Least Frequently Used),這種政策根據最近通路的頻率來進行淘汰,如果空間不足,會釋放最近通路頻率最低的對象。這個算法也是用優先隊列實作的比較常見。
分布式緩存的常見問題
緩存穿透
- DB中不存在資料,每次都穿過緩存查DB,造成DB的壓力。一般是網絡攻擊
- 解決方案:放入一個特殊對象(比如特定的無效對象,當然比較好的方式是使用包裝對象)
代碼示例
緩存擊穿
- 在緩存失效的瞬間大量請求,造成DB的壓力瞬間增大
- 解決方案:更新緩存時使用分布式鎖鎖住服務,防止請求穿透直達DB
緩存雪崩
- 大量緩存設定了相同的失效時間,同一時間失效,造成服務瞬間性能急劇下降
- 解決方案:緩存時間使用基本時間加上随機時間
參考資料
- 緩存的三種方案