Redis快取雪崩、快取穿透、快取擊穿對比看這一篇就夠了

pengjianglilive發表於2020-10-28

 

快取穿透

什麼是快取穿透?

快取穿透是指使用者請求的資料在快取中不存在即沒有命中,同時在資料庫中也不存在,導致使用者每次請求該資料都要去資料庫中查詢一遍,然後返回空。

如果有惡意攻擊者不斷請求系統中不存在的資料,會導致短時間大量請求落在資料庫上,造成資料庫壓力過大,甚至擊垮資料庫系統。

快取穿透常用的解決方案

(1)布隆過濾器(推薦)

布隆過濾器(Bloom Filter,簡稱BF)由Burton Howard Bloom在1970年提出,是一種空間效率高的概率型資料結構。

布隆過濾器專門用來檢測集合中是否存在特定的元素。

如果在平時我們要判斷一個元素是否在一個集合中,通常會採用查詢比較的方法,下面分析不同的資料結構查詢效率:

  • 採用線性表儲存,查詢時間複雜度為O(N)

  • 採用平衡二叉排序樹(AVL、紅黑樹)儲存,查詢時間複雜度為O(logN)

  • 採用雜湊表儲存,考慮到雜湊碰撞,整體時間複雜度也要O[log(n/m)]

當需要判斷一個元素是否存在於海量資料集合中,不僅查詢時間慢,還會佔用大量儲存空間。接下來看一下布隆過濾器如何解決這個問題。

布隆過濾器設計思想

布隆過濾器由一個長度為m位元的位陣列(bit array)與k個雜湊函式(hash function)組成的資料結構。位陣列初始化均為0,所有的雜湊函式都可以分別把輸入資料儘量均勻地雜湊。

當要向布隆過濾器中插入一個元素時,該元素經過k個雜湊函式計算產生k個雜湊值,以雜湊值作為位陣列中的下標,將所有k個對應的位元值由0置為1。

當要查詢一個元素時,同樣將其經過雜湊函式計算產生雜湊值,然後檢查對應的k個位元值:如果有任意一個位元為0,表明該元素一定不在集合中;如果所有位元均為1,表明該集合有可能性在集合中。為什麼不是一定在集合中呢?因為不同的元素計算的雜湊值有可能一樣,會出現雜湊碰撞,導致一個不存在的元素有可能對應的位元位為1,這就是所謂“假陽性”(false positive)。相對地,“假陰性”(false negative)在BF中是絕不會出現的。

總結一下:布隆過濾器認為不在的,一定不會在集合中;布隆過濾器認為在的,可能在也可能不在集合中。

舉個例子:下圖是一個布隆過濾器,共有18個位元位,3個雜湊函式。集合中三個元素x,y,z通過三個雜湊函式雜湊到不同的位元位,並將位元位置為1。當查詢元素w時,通過三個雜湊函式計算,發現有一個位元位的值為0,可以肯定認為該元素不在集合中。

圖片

圖片

布隆過濾器優缺點

優點:

  • 節省空間:不需要儲存資料本身,只需要儲存資料對應hash位元位

  • 時間複雜度低:插入和查詢的時間複雜度都為O(k),k為雜湊函式的個數

缺點:

  • 存在假陽性:布隆過濾器判斷存在,可能出現元素不在集合中;判斷準確率取決於雜湊函式的個數

  • 不能刪除元素:如果一個元素被刪除,但是卻不能從布隆過濾器中刪除,這也是造成假陽性的原因了

布隆過濾器適用場景

  • 爬蟲系統url去重

  • 垃圾郵件過濾

  • 黑名單

(2)返回空物件

當快取未命中,查詢持久層也為空,可以將返回的空物件寫到快取中,這樣下次請求該key時直接從快取中查詢返回空物件,請求不會落到持久層資料庫。為了避免儲存過多空物件,通常會給空物件設定一個過期時間。

這種方法會存在兩個問題:

  • 如果有大量的key穿透,快取空物件會佔用寶貴的記憶體空間。

  • 空物件的key設定了過期時間,在這段時間可能會存在快取和持久層資料不一致的場景。

快取擊穿

什麼是快取擊穿?

快取擊穿,是指一個key非常熱點,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破快取,直接請求資料庫,就像在一個屏障上鑿開了一個洞。

快取擊穿危害

資料庫瞬時壓力驟增,造成大量請求阻塞。

如何解決

使用互斥鎖(mutex key)

這種思路比較簡單,就是讓一個執行緒回寫快取,其他執行緒等待回寫快取執行緒執行完,重新讀快取即可。

圖片

圖片

同一時間只有一個執行緒讀資料庫然後回寫快取,其他執行緒都處於阻塞狀態。如果是高併發場景,大量執行緒阻塞勢必會降低吞吐量。這種情況如何解決?大家可以在留言區討論。

如果是分散式應用就需要使用分散式鎖。

熱點資料永不過期

永不過期實際包含兩層意思:

  • 物理不過期,針對熱點key不設定過期時間

  • 邏輯過期,把過期時間存在key對應的value裡,如果發現要過期了,通過一個後臺的非同步執行緒進行快取的構建

圖片

圖片

從實戰看這種方法對於效能非常友好,唯一不足的就是構建快取時候,其餘執行緒(非構建快取的執行緒)可能訪問的是老資料,對於不追求嚴格強一致性的系統是可以接受的。

快取雪崩

什麼是快取雪崩?

快取雪崩是指快取中資料大批量到過期時間,而查詢資料量巨大,請求直接落到資料庫上,引起資料庫壓力過大甚至當機。和快取擊穿不同的是,快取擊穿指併發查同一條資料,快取雪崩是不同資料都過期了,很多資料都查不到從而查資料庫。

快取雪崩解決方案

常用的解決方案有:

  • 均勻過期

  • 加互斥鎖

  • 快取永不過期

  • 雙層快取策略

(1)均勻過期

設定不同的過期時間,讓快取失效的時間點儘量均勻。通常可以為有效期增加隨機值或者統一規劃有效期。

(2)加互斥鎖

跟快取擊穿解決思路一致,同一時間只讓一個執行緒構建快取,其他執行緒阻塞排隊。

(3)快取永不過期

跟快取擊穿解決思路一致,快取在物理上永遠不過期,用一個非同步的執行緒更新快取。

(4)雙層快取策略

使用主備兩層快取:

主快取:有效期按照經驗值設定,設定為主讀取的快取,主快取失效後從資料庫載入最新值。

備份快取:有效期長,獲取鎖失敗時讀取的快取,主快取更新時需要同步更新備份快取。

快取預熱

什麼是快取預熱?

快取預熱就是系統上線後,將相關的快取資料直接載入到快取系統,這樣就可以避免在使用者請求的時候,先查詢資料庫,然後再將資料回寫到快取。

如果不進行預熱, 那麼 Redis 初始狀態資料為空,系統上線初期,對於高併發的流量,都會訪問到資料庫中, 對資料庫造成流量的壓力。

快取預熱的操作方法

  • 資料量不大的時候,工程啟動的時候進行載入快取動作;

  • 資料量大的時候,設定一個定時任務指令碼,進行快取的重新整理;

  • 資料量太大的時候,優先保證熱點資料進行提前載入到快取。

快取降級

快取降級是指快取失效或快取伺服器掛掉的情況下,不去訪問資料庫,直接返回預設資料或訪問服務的記憶體資料。

在專案實戰中通常會將部分熱點資料快取到服務的記憶體中,這樣一旦快取出現異常,可以直接使用服務的記憶體資料,從而避免資料庫遭受巨大壓力。

降級一般是有損的操作,所以儘量減少降級對於業務的影響程度。

 

吐血推薦:

【玩轉Redis面試第1講】Redis資料結構和常用命令速記

【玩轉Redis面試第2講】面試官再問Redis事務把這篇文章扔給他

【玩轉Redis面試第3講】一次性將Redis RDB持久化和AOF持久化講透

相關文章