作者:漫話編程 來自:漫話編程
周末在家面試,和候選人聊到Redis的問題,於是問了他一個問題:你知道緩存穿透、緩存擊穿和緩存雪崩嗎?他們之間的區別是什麼?分別怎麼解決嗎?
面試結束後,女朋友好像有很多問號,於是來問我。
緩存穿透
要把這個問題講清楚,先舉個例子。
一個女孩子去門店買口紅,到了門店之後被告知她想要的那個色號已經沒有了。於是她要求店員去問總部還有沒有貨。總部發現這個色號也沒有了,於是女孩子就離開了。
過了一會另一個女孩子又來了,也想要購買同一個色號,店員就又總部問了一次。如此反覆。
女孩子買口紅不僅需要門店幫忙查詢,還需要總部也進行盤貨。類似這種情況,在緩存領域有一個類似的概念叫做緩存穿透。
緩存穿透是指緩存伺服器中沒有緩存數據,資料庫中也沒有符合條件的數據,導致業務系統每次都繞過緩存伺服器查詢下遊的資料庫,緩存伺服器完全失去了其應用的作用。
緩存空值
解決多次詢問總部的方法比較簡單,如果口紅門店在幫第一個女孩子查詢之後,就記錄下來這個色號已經沒有了,下次其他女孩再來問這個色號的時候,直接告訴她沒貨了。
這樣就可以避免每次都驚動總部了。
在緩存中,之所以會發生穿透,就是因為緩存沒有對那些不存在的值得Key緩存下來,從而導致每次查詢都要請求到資料庫。
那麼我們就可以為這些key對應的值設置為null並放到緩存中,這樣再出現查詢這個key 的請求的時候,直接返回null即可 。
但是還需要注意的就是需要有一個失效時間,因為如果不設置失效的話,如果哪天總部有貨了,門店還是當做沒貨的話,就會影響銷量了。
BloomFilter
很多時候,緩存穿透是因為有很多惡意流量的請求,這些請求可能隨機生成很多Key來請求查詢,這些肯定在緩存和資料庫中都沒有,那就很容易導致緩存穿透。
針對類似的情況,可以使用一個過濾器。
比如如果有一群人經常來門店問一些根本不存在的色號,比如五彩斑斕的黑,這些色號該品牌根本沒生產過的話,店員就可以直接告訴顧客不存在就行了,也不需要驚動總部。
在緩存穿透防治上常用的技術是布隆過濾器(Bloom Filter)。
布隆過濾器是一種比較巧妙的概率性數據結構,它可以告訴你數據一定不存在或可能存在,相比Map、Set、List等傳統數據結構它佔用內存少、結構更高效。
對於緩存穿透,我們可以將查詢的數據條件都哈希到一個足夠大的布隆過濾器中,用戶發送的請求會先被布隆過濾器攔截,一定不存在的數據就直接攔截返回了,從而避免下一步對資料庫的壓力。
緩存擊穿
有一種比較特殊的情況,那就是如果某一個熱門色號的口紅剛好賣完了,這時候有很多顧客同時來諮詢要購買這個色號,那麼門店內的多個售貨員可能分別給總部打電話諮詢是否有存貨。
或者如果有多家門店同時賣完了,那麼總部接收到的諮詢量就會劇增。類似這種情況,在緩存領域有一個類似的概念叫做緩存擊穿。
緩存擊穿是指當某一key的緩存過期時大並發量的請求同時訪問此key,瞬間擊穿緩存伺服器直接訪問資料庫,讓資料庫處於負載的情況。
異步定時更新
如果提前知道某一個色號比較暢銷的話,那就可以定時的諮詢總部是否還有存貨,定時的更新庫存情況就可以避免上面這種情況了。
在緩存處理上,同理,比如某一個熱點數據的過期時間是1小時,那麼每59分鐘,通過定時任務去更新這個熱點key,並重新設置其過期時間。
互斥鎖
還有一種解決辦法,那就是如果很多顧客諮詢的是同一個色號的口紅,那麼就先處理第一個用戶的諮詢,其他同樣請求的顧客先排隊等待。一直到店員從總部那裡獲取到最新的庫存信息後,就可以安排其他人繼續購買了。
在緩存處理上,通常使用一個互斥鎖來解決緩存擊穿的問題。簡單來說就是當Redis中根據key獲得的value值為空時,先鎖上,然後從資料庫加載,加載完畢,釋放鎖。若其他線程也在請求該key時,發現獲取鎖失敗,則先阻塞。
緩存雪崩
如果門店內的多個色號的口紅同時售罄了,並且門店在這個時間點剛好也不知道總部有沒有庫存了,這時候如果有大量顧客來到門店購物的話,就會有更多的諮詢電話打到總部那裡。
或者是門店突然出現問題了,不能提供服務了,很多顧客就可能自己打電話到總部諮詢庫存情況。類似這種情況,在緩存領域有一個類似的概念叫做緩存雪崩。
緩存雪崩是指當大量緩存同時過期或緩存服務宕機,所有請求的都直接訪問資料庫,造成資料庫高負載,影響性能,甚至資料庫宕機。
不同的過期時間
為了避免緩存雪崩,門店可以考慮給不同的色號的口紅預留不同的庫存,並且採用不同的頻率諮詢總部庫存情況,更新到門店中。這樣就可以避免突然同一個時間點所有色號都售罄。
為了避免大量的緩存在同一時間過期,可以把不同的key過期時間設置成不同的, 並且通過定時刷新的方式更新過期時間。
集群
為了避免門店出問題導致大量顧客直接打電話到總部,可以考慮開更多的門店,將用戶分流到多個店鋪中。
類似的,在緩存雪崩問題防治上面,一個比較典型的技術就是採用集群方式部署,使用集群可以避免服務單點故障。