什么是 redis 的雪崩、穿透和擊穿?


出處: 什么是 redis 的雪崩、穿透和擊穿?

 

  關於Redis雪崩,穿透,擊穿的問題,第一次接觸名字有點陌生,聽上去還比較相似,難以理解,過去做的很多項目中也都是用過Redis,但是第一次聽到這幾個關於Redis的坑還是一臉懵逼,直到這些坑真正顯靈的時候才徹底意識到搞明白。

  第一次線上遇到 Redis 雪崩的時候我是維護某電影線上平台(某首富家的)國內某電影平台,因為系統架構老舊,每年大年初一賀歲檔電影上映,購票人達到高峰,系統就會發生因緩存失效問題導致的出票故障,當時底層是用的oracle小型機,依舊扛不住。大年初一,所有同事打開電腦被迫營業,關於緩存失效引起的類似問題,堅決要扼殺,對用戶友好,對程序員友好,對老板也友好。

1.面試官:關於Redis雪崩,穿透,擊穿你是怎么理解的?

Redis 雪崩:

  雪崩就是指緩存中大批量熱點數據過期后系統涌入大量查詢請求,因為大部分數據在Redis層已經失效,請求滲透到數據庫層,大批量請求猶如洪水一般涌入,引起數據庫壓力造成查詢堵塞甚至宕機。

解決辦法:

  1. 將緩存失效時間分散開,比如每個key的過期時間是隨機,防止同一時間大量數據過期現象發生,這樣不會出現同一時間全部請求都落在數據庫層,如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同Redis和數據庫中,有效分擔壓力,別一個人扛。
  2. 簡單粗暴,讓Redis數據永不過期(如果業務准許,比如不用更新的名單類)。當然,如果業務數據准許的情況下可以,比如中獎名單用戶,每期用戶開獎后,名單不可能會變了,無需更新。

面試題

  了解什么是 redis 的雪崩、穿透和擊穿?redis 崩潰之后會怎么樣?系統該如何應對這種情況?如何處理 redis 的穿透?

面試官心理分析

  其實這是問到緩存必問的,因為緩存雪崩和穿透,是緩存最大的兩個問題,要么不出現,一旦出現就是致命性的問題,所以面試官一定會問你。

面試題剖析

緩存雪崩

  對於系統 A,假設每天高峰期每秒 5000 個請求,本來緩存在高峰期可以扛住每秒 4000 個請求,但是緩存機器意外發生了全盤宕機。緩存掛了,此時 1 秒 5000 個請求全部落數據庫,數據庫必然扛不住,它會報一下警,然后就掛了。此時,如果沒有采用什么特別的方案來處理這個故障,DBA 很着急,重啟數據庫,但是數據庫立馬又被新的流量給打死了。

  這就是緩存雪崩。

  大約在 3 年前,國內比較知名的一個互聯網公司,曾因為緩存事故,導致雪崩,后台系統全部崩潰,事故從當天下午持續到晚上凌晨 3~4 點,公司損失了幾千萬。

  緩存雪崩的事前事中事后的解決方案如下。 - 事前:redis 高可用,主從+哨兵,redis cluster,避免全盤崩潰。 - 事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死。 - 事后:redis 持久化,一旦重啟,自動從磁盤上加載數據,快速恢復緩存數據。

 

 

  用戶發送一個請求,系統 A 收到請求后,先查本地 ehcache 緩存,如果沒查到再查 redis。如果 ehcache 和 redis 都沒有,再查數據庫,將數據庫中的結果,寫入 ehcache 和 redis 中。

  限流組件,可以設置每秒的請求,有多少能通過組件,剩余的未通過的請求,怎么辦?走降級!可以返回一些默認的值,或者友情提示,或者空白的值。

  好處: - 數據庫絕對不會死,限流組件確保了每秒只有多少個請求能通過。 - 只要數據庫不死,就是說,對用戶來說,2/5 的請求都是可以被處理的。 - 只要有 2/5 的請求可以被處理,就意味着你的系統沒死,對用戶來說,可能就是點擊幾次刷不出來頁面,但是多點幾次,就可以刷出來一次。

緩存穿透

  對於系統A,假設一秒 5000 個請求,結果其中 4000 個請求是黑客發出的惡意攻擊。

  黑客發出的那 4000 個攻擊,緩存中查不到,每次你去數據庫里查,也查不到。

  舉個栗子。數據庫 id 是從 1 開始的,結果黑客發過來的請求 id 全部都是負數。這樣的話,緩存中不會有,請求每次都“視緩存於無物”,直接查詢數據庫。這種惡意攻擊場景的緩存穿透就會直接把數據庫給打死。

 

  解決方式很簡單,每次系統 A 從數據庫中只要沒查到,就寫一個空值到緩存里去,比如 set -999 UNKNOWN。然后設置一個過期時間,這樣的話,下次有相同的 key 來訪問的時候,在緩存失效之前,都可以直接從緩存中取數據。

緩存擊穿

  緩存擊穿,就是說某個 key 非常熱點,訪問非常頻繁,處於集中式高並發訪問的情況,當這個 key 在失效的瞬間,大量的請求就擊穿了緩存,直接請求數據庫,就像是在一道屏障上鑿開了一個洞。

  解決方式也很簡單,可以將熱點數據設置為永遠不過期;或者基於 redis or zookeeper 實現互斥鎖,等待第一個請求構建完緩存之后,再釋放鎖,進而其它請求才能通過該 key 訪問數據。

public String get(key) {
    String value = redis.get(key);
    if (value == null) { //代表緩存值過期
        //設置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load db
        if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表設置成功
            value = db.get(key);
            redis.set(key, value, expire_secs);
            redis.del(key_mutex);
        } else {  //這個時候代表同時候的其他線程已經load db並回設到緩存了,這時候重試獲取緩存值即可
            sleep(50);
            get(key);  //重試
        }
    else {
        return value;      
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM