大家好,我是老三,刷題刷煩了……
寫篇文章緩一下,分享一下老八股:快取擊穿
、快取穿透
、快取雪崩
。
在瞭解這三大問題之前,我們要理解,常用的分散式快取Redis單機併發量能達到萬級,常用的關係型資料庫MySQL一般併發量是千級,他們支援的併發量可能差十倍,所以要儘可能把流量攔截在快取層。
為什麼呢?就像是大湖裡多排點水,就可能把小河道沖垮,不知道你聽過沒——長江之水天上來,白洋澱裡把不住。
快取擊穿
什麼是快取擊穿
先從快取擊穿開始。
快取擊穿: 一個併發訪問量比較大的key在某個時間過期,導致所有的請求直接打在DB上。
快取擊穿會增大資料庫的負載,我們看看怎麼緩解。
快取擊穿如何解決
加鎖更新
查詢快取,發現快取中不存在,加鎖,讓其它執行緒等待,只讓一個執行緒去更新快取。
非同步更新
還有一個可行的方案是把快取設定永不過期。那快取怎麼更新呢?通過非同步的方式去更新快取。
比如後臺設定一個守護執行緒定時更新快取,但這種定時比較難以把握。
非同步更新機制實際上更適合用於快取預熱。
快取穿透
什麼是快取穿透
快取穿透:快取穿透指的查詢快取和資料庫中都不存在的資料,這樣每次請求直接打到資料庫,就好像快取不存在一樣。
快取穿透將導致不存在的資料每次請求都要到儲存層去查詢,失去了快取保護後端儲存的意義。
快取穿透可能會使後端儲存負載加大,如果發現大量儲存層空命中,可能就是出現了快取穿透問題。
快取穿透可能有兩種原因:
- 自身業務程式碼問題
- 惡意攻擊,爬蟲造成空命中
我們來看看如何解決。
快取穿透如何解決
快取空值/預設值
一種方式是在資料庫不命中之後,把一個空物件或者預設值儲存到快取,之後再訪問這個資料,就會從快取中獲取,這樣就保護了資料庫。
快取空值有兩大問題:
-
空值做了快取,意味著快取層中存了更多的鍵,需要更多的記憶體空間(如果是攻擊,問題更嚴重),比較有效的
方法是針對這類資料設定一個較短的過期時間,讓其自動剔除。
-
快取層和儲存層的資料會有一段時間視窗的不一致,可能會對業務有一定影響。
例如過期時間設定為5分鐘,如果此時儲存層新增了這個資料,那此段時間就會出現快取層和儲存層資料的不一致。
這時候可以利用訊息佇列或者其它非同步方式清理快取中的空物件。
布隆過濾器
除了快取空物件,我們還可以在儲存和快取之前,加一個布隆過濾器,做一層過濾。
布隆過濾器裡會儲存資料是否存在,如果判斷資料不不能再,就不會訪問儲存。
那布隆過濾器是什麼玩意兒?查詢它會不會很慢?
布隆過濾器是什麼?
不知道你對雜湊表瞭解多少,布隆過濾器是一個類似的東西。
它是一個連續的資料結構,每個儲存位儲存都是一個bit
,即0
或者1
, 來標識資料是否存在。
儲存資料的時時候,使用K個不同的雜湊函式將這個變數對映為bit列表的的K個點,把它們置為1。
我們判斷快取key是否存在,同樣,K個雜湊函式,對映到bit列表上的K個點,判斷是不是1:
- 如果全不是1,那麼key不存在;
- 如果都是1,也只是表示key可能存在。
至於為什麼?因為雜湊函式是存在碰撞的可能的。
關於快取穿透的兩種主要解決方案,我們簡單對比一下:
快取雪崩
接下來我們看最嚴重的一種情況,快取雪崩。
什麼是快取雪崩
快取雪崩: 當某⼀時刻發⽣⼤規模的快取失效的情況,例如快取服務當機、大量key在同一時間過期,這樣的後果就是⼤量的請求進來直接打到DB上,可能導致整個系統的崩潰,稱為雪崩。
快取雪崩如何解決
快取雪崩是三大快取問題裡最嚴重的一種,我們來看看怎麼預防和處理。
提高快取可用性
- 叢集部署:通過叢集來提升快取的可用性,可以利用Redis本身的Redis Cluster或者第三方叢集方案如Codis等。
- 多級快取:設定多級快取,第一級快取失效的基礎上,訪問二級快取,每一級快取的失效時間都不同。
過期時間
- 均勻過期:為了避免大量的快取在同一時間過期,可以把不同的 key 過期時間隨機生成,避免過期時間太過集中。
- 熱點資料永不過期。
熔斷降級
- 服務熔斷:當快取伺服器當機或超時響應時,為了防止整個系統出現雪崩,暫時停止業務服務訪問快取系統。
- 服務降級:當出現大量快取失效,而且處在高併發高負荷的情況下,在業務系統內部暫時捨棄對一些非核心的介面和資料的請求,而直接返回一個提前準備好的 fallback(退路)錯誤處理資訊。
總結
一張圖總結:
參考:
[1]. 《Redis開發與運維》
[2]. 《極客時間 高併發系統設計四十問》
[5]. 布隆過濾器,沒那麼難