快取擊穿、快取穿透、快取雪崩這三個問題是Reids在實際專案中會經常遇到問題,同時,這三個問題也是面試的熱點問題,下面,就本篇文章搞懂快取穿透、快取擊穿、快取雪崩三大問題的原因及解決方法
首先看一下Redis在專案中作為快取中介軟體是如何工作的
使用者端發起一個查詢請求的時候,首先去快取中查詢,如果資料在快取中存在,則直接將快取中的資料返回給使用者端;如果資料在快取中不存在,則繼續查詢資料庫,如果資料在資料庫中存在,則將該資料放入快取中,並返回給使用者端,如果資料在資料庫中也不存在,則直接返回null給使用者端。
快取穿透是指查詢一個快取中和資料庫中都不存在的資料,導致每次查詢這條資料都會透過快取,直接查庫,最後返回空。當用戶使用這條不存在的資料瘋狂發起查詢請求的時候,對資料庫造成的壓力就非常大,甚至可能直接掛掉。這種情況的流程就變成下圖這樣了:
解決快取穿透的方法一般有兩種,第一種是快取空物件,第二種是使用布隆過濾器。
第一種方法比較好理解,就是當資料庫中查不到資料的時候,我快取一個空物件,然後給這個空物件的快取設定一個過期時間,這樣下次再查詢該資料的時候,就可以直接從快取中拿到,從而達到了減小資料庫壓力的目的。但這種解決方式有兩個缺點:(1)需要快取層提供更多的記憶體空間來快取這些空物件,當這種空物件很多的時候,就會浪費更多的記憶體;(2)會導致快取層和儲存層的資料不一致,即使在快取空物件時給它設定了一個很短的過期時間,那也會導致這一段時間內的資料不一致問題。
使用布隆過濾器,這是比較推薦的方法。所謂布隆過濾器,就是一種資料結構,它是由一個長度為m bit的位陣列與n個hash函陣列成的資料結構,位陣列中每個元素的初始值都是0。在初始化布隆過濾器時,會先將所有key進行n次hash運算,這樣就可以得到n個位置,然後將這n個位置上的元素改為1。這樣,就相當於把所有的key儲存到了布隆過濾器中了。
舉個例子,比如我們一共有3個key,我們對這3個key分別進行3次hash運算,key1經過三次hash運算後的結果分別為2/6/10,那麼就把布隆過濾器中下標為2/6/10的元素值更新為1,然後再分別對key2和key3做同樣操作,結果如下圖:
這樣,當用戶端查詢時,也對查詢的key做3次hash運算得到3個位置,然後看布隆過濾器中對應位置元素的值是否為1,如果所有對應位置元素的值都為1,就證明key在庫中存在,則繼續向下查詢;如果3個位置中有任意一個位置的值不為1,那麼就證明key在庫中不存在,直接返回使用者端空即可。如下圖:
當用戶端查詢key4時,key4的3次hash運算中,有一個位置8的值為0,就說明key4在庫中不存在,直接返回使用者端空即可。
所以,布隆過濾器就相當於一個位於使用者端與快取層中間的攔截器一樣,負責判斷key是否在集合中存在。如下圖:
布隆過濾器的好處就是解決了第一種快取空值的不足,但布隆過濾器也存在缺陷,首先,它有誤判的可能,比如在上面使用者端查詢key4的圖中,假如key4經過3次hash運算得到的位置分別是2/4/6,由於這3個位置的值都是1,所以,布隆過濾器就認為key4在庫中存在,進而繼續向下查詢了。所以,布隆過濾器判斷存在的key實際上可能是不存在的,但布隆過濾器判斷不存在的key是一定不存在的。它的第二個缺點就是刪除元素比較難,比如現在要刪除key2這個元素,那麼需要將2/7/11三個位置的元素值改為0,但這樣就會影響到key1和key3的判斷。
快取擊穿是指當快取中某個熱點資料過期了,在該熱點資料重新載入快取之前,有大量的查詢請求穿過快取,直接查詢資料庫。這種情況會導致資料庫壓力瞬間驟增,造成大量請求阻塞,甚至直接掛掉。
解決快取擊穿的方法也有兩種,第一種是設定key永不過期;第二種是使用分散式鎖,保證同一時刻只能有一個查詢請求重新載入熱點資料到快取中,這樣,其他的執行緒只需等待該執行緒執行完畢,即可重新從Redis中獲取資料。
第一種方式比較簡單,在設定熱點key的時候,不給key設定過期時間即可。不過還有另外一種方式也可以達到key不過期的目的,就是正常給key設定過期時間,不過在後臺同時啟一個定時任務去定時地更新這個快取。
第二種方式使用了加鎖的方式,鎖的物件就是key,這樣,當大量查詢同一個key的請求並行進來時,只能有一個請求獲取到鎖,然後獲取到鎖的執行緒查詢資料庫,然後將結果放入到快取中,然後釋放鎖,此時,其他處於鎖等待的請求即可繼續執行,由於此時快取中已經有了資料,所以直接從快取中獲取到資料返回,並不會查詢資料庫
快取雪崩是指當快取中有大量的key在同一時刻過期,或者Redis直接宕機了,導致大量的查詢請求全部到達資料庫,造成資料庫查詢壓力驟增,甚至直接掛掉。
針對第一種大量key同時過期的情況,解決起來比較簡單,只需要將每個key的過期時間打散即可,使它們的失效點儘可能均勻分佈。
針對第二種redis發生故障的情況,部署redis時可以使用redis的幾種高可用方案部署
除了上面兩種解決方式,還可以使用其他策略,比如設定key永不過期、加分散式鎖等。