緩存穿透
什么是緩存穿透
緩存穿透說簡單點就是大量請求的 key 根本不存在于緩存中,導致請求直接到了數據庫上,根本沒有經過緩存這一層。舉個例子:某個黑客故意制造我們緩存中不存在的 key 發起大量請求,導致大量請求落到數據庫。
緩存穿透的大致流程
如下圖所示,用戶的請求最終都要跑到數據庫中查詢一遍。
?解決辦法
最基本的就是首先做好參數校驗,一些不合法的參數請求直接拋出異常信息返回給客戶端。比如查詢的數據庫 id 不能小于 0、傳入的郵箱格式不對的時候直接返回錯誤消息給客戶端等等。
1)緩存無效 key
如果緩存和數據庫都查不到某個 key 的數據就寫一個到 Redis 中去并設置過期時間,具體命令如下:?SET key value EX 10086
?。這種方式可以解決請求的 key 變化不頻繁的情況,如果黑客惡意攻擊,每次構建不同的請求 key,會導致 Redis 中緩存大量無效的 key 。很明顯,這種方案并不能從根本上解決此問題。如果非要用這種方式來解決穿透問題的話,盡量將無效的 key 的過期時間設置短一點比如 1 分鐘。
另外,這里多說一嘴,一般情況下我們是這樣設計 key 的:?表名:列名:主鍵名:主鍵值
?。
2)布隆過濾器
布隆過濾器是一個非常神奇的數據結構,通過它我們可以非常方便地判斷一個給定數據是否存在于海量數據中。我們需要的就是判斷 key 是否合法,有沒有感覺布隆過濾器就是我們想要找的那個“人”。
具體是這樣做的:把所有可能存在的請求的值都存放在布隆過濾器中,當用戶請求過來,先判斷用戶發來的請求的值是否存在于布隆過濾器中。不存在的話,直接返回請求參數錯誤信息給客戶端,存在的話才會走下面的流程。
加入布隆過濾器之后的緩存處理流程圖如下。
但是,需要注意的是布隆過濾器可能會存在誤判的情況。總結來說就是:?布隆過濾器說某個元素存在,小概率會誤判。布隆過濾器說某個元素不在,那么這個元素一定不在。
為什么會出現誤判的情況呢? 我們還要從布隆過濾器的原理來說!
我們先來看一下,當一個元素加入布隆過濾器中的時候,會進行哪些操作:
- 使用布隆過濾器中的哈希函數對元素值進行計算,得到哈希值(有幾個哈希函數得到幾個哈希值)。
- 根據得到的哈希值,在位數組中把對應下標的值置為 1。
我們再來看一下,當我們需要判斷一個元素是否存在于布隆過濾器的時候,會進行哪些操作:
- 對給定元素再次進行相同的哈希計算;
- 得到值之后判斷位數組中的每個元素是否都為 1,如果值都為 1,那么說明這個值在布隆過濾器中,如果存在一個值不為 1,說明該元素不在布隆過濾器中。
然后,一定會出現這樣一種情況:不同的字符串可能哈希出來的位置相同。?(可以適當增加位數組大小或者調整我們的哈希函數來降低概率)
緩存擊穿
什么是緩存擊穿
緩存擊穿是指在緩存系統中,某個被大量訪問的熱點數據在緩存中的有效期剛好到期,而此時大量并發請求同時過來訪問該數據,由于緩存中已無此數據,這些請求就會同時繞過緩存直接去訪問數據庫,從而導致數據庫瞬間承受巨大的查詢壓力,可能會影響數據庫甚至整個系統的正常運行。
解決方案
常見的解決方案有兩種:
互斥鎖
互斥鎖的解決思路就是在Redis進行緩存重建時,拿到一個互斥鎖,其他請求拿不到這個鎖就是乖乖等待鎖的釋放。
邏輯過期
邏輯過期的解決思路如下:
在存入redis的value中增加一個字段,該字段為過期時間加上x分鐘,通過計算就知道這個數據是否邏輯上過期,事實上沒過期一直存在redis中。
在redis進行緩存重建的時候,會另開一個線程進行重建并拿到互斥鎖,其他線程拿不到數據想要緩存重建時也拿不到鎖,那就直接返回舊數據。
互斥鎖方案:由于保證了互斥性,所以數據一致,且實現簡單,因為僅僅只需要加一把鎖而已,也沒其他的事情需要操心,所以沒有額外的內存消耗,缺點在于有鎖就有死鎖問題的發生,且只能串行執行性能肯定受到影響
邏輯過期方案: 線程讀取過程中不需要等待,性能好,有一個額外的線程持有鎖去進行重構數據,但是在重構數據完成前,其他的線程只能返回之前的數據,且實現起來麻煩
緩存雪崩
什么是緩存雪崩?
實際上,緩存雪崩描述的就是這樣一個簡單的場景:緩存在同一時間大面積的失效,后面的請求都直接落到了數據庫上,造成數據庫短時間內承受大量請求。?這就好比雪崩一樣,摧枯拉朽之勢,數據庫的壓力可想而知,可能直接就被這么多請求弄宕機了。
舉個例子:系統的緩存模塊出了問題比如宕機導致不可用。造成系統的所有訪問,都要走數據庫。
還有一種緩存雪崩的場景是:有一些被大量訪問數據(熱點緩存)在某一時刻大面積失效,導致對應的請求直接落到了數據庫上。?這樣的情況,有下面幾種解決辦法:
舉個例子 :秒殺開始 12 個小時之前,我們統一存放了一批商品到 Redis 中,設置的緩存過期時間也是 12 個小時,那么秒殺開始的時候,這些秒殺的商品的訪問直接就失效了。導致的情況就是,相應的請求直接就落到了數據庫上,就像雪崩一樣可怕。
解決辦法
(1)給不同的Key的TTL添加隨機值(推薦)
? ? ? ? 操作簡單,當我們在做緩存預熱的時候,就有可能在同一時間批量插入大量的數據,
那么如果它們的TTL都一樣的話就可能出現大量key同時過期的情況!!!
所以我們需要在設置過期時間TTL的時候,定義一個范圍,追加該范圍內的一個隨機數。
(2)利用Redis集群提高服務的可用性
? ? ? ? 使用集群提高可靠性
(3)給緩存業務添加降級限流策略
? ? ? ? 微服務的知識
(4)給業務添加多級緩存 ?
? ? ? ? 請求到達瀏覽器,nginx可以做緩存,未命中找Redis,再未命中找JVM,最后到數據庫......
?總結
我個人感覺redis的緩存穿透和緩存擊穿有部分是類似,而緩存擊穿和緩存雪崩又有一部分是類似的
緩存穿透和緩存擊穿的大至區別
- 緩存擊穿:指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由于并發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力。
- 緩存穿透:指查詢一個根本不存在的數據,緩存層和存儲層都不會命中,每次都會去請求數據庫,若有大量這樣的請求,可能會導致數據庫壓力過大甚至崩潰。
緩存擊穿和緩存雪崩的大至區別
- 涉及數據范圍:緩存擊穿主要針對的是單個熱點數據,是由于單個關鍵數據的緩存過期,大量請求同時訪問這一數據而引發問題;而緩存雪崩涉及的是大量數據,是大量緩存數據在同一時間或短時間內集體失效,導致大量請求沖向數據庫。
- 引發原因側重:緩存擊穿更多是因為某個熱點數據的緩存時間設置不當,或者在緩存過期的瞬間有大量并發請求訪問;緩存雪崩除了可能因為緩存時間設置不合理外,還可能由于緩存服務器故障、大量數據同時更新等原因導致大量緩存數據同時失效。
- 問題表現形式:緩存擊穿是大量請求集中訪問某一個特定數據;緩存雪崩是大量請求分散地訪問多個不同的數據,但這些數據的緩存同時失效,導致整體請求流量對數據庫造成巨大沖擊。
文章參考
Redis的緩存穿透、緩存雪崩、緩存擊穿問題及有效解決方案_緩存雪崩和緩存穿透問題解決方案-CSDN博客