<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊聊Redis熱點key緩存問題如何解決

          共 3750字,需瀏覽 8分鐘

           ·

          2022-05-18 15:51

          本文分享一篇關(guān)于Redis熱門問題,緩存穿透、緩存擊穿和緩存雪崩。針對該文的面試題,我也整理好了,你可以直接點擊查看。

          說明

          緩存穿透、緩存擊穿和緩存雪崩是Redis面試當(dāng)中和實際開發(fā)中,經(jīng)常需要考慮的一個問題。很多人對該問題的產(chǎn)生、原因和解決方案還是不夠清晰。其實大家針對該三種情況,去仔細(xì)分析一個產(chǎn)生的原理就能很好的找到一個好的解決方案。

          本文通過定義、案例、危害和解決方案的幾個角度,來幫助你快速了解該三個問題。

          相信大家在網(wǎng)上也看到很多解決這三種問題的解決方案,其中的一些方案是否是一個正確的方案呢?本文也將一一分析此類方案的優(yōu)缺點。

          下圖為本文的內(nèi)容大綱,文章也是圍繞這幾點進(jìn)行分析與總結(jié)。

          三者比較

          1. 1. 緩存穿透、緩存擊穿和緩存雪崩都是因為緩存中數(shù)據(jù)不存在,導(dǎo)致走數(shù)據(jù)庫去查詢數(shù)據(jù)。

          2. 2. 由于緩存數(shù)據(jù)不存在,所有的請求都會走到數(shù)據(jù)庫,因此會導(dǎo)致數(shù)據(jù)庫的壓力過大甚至出現(xiàn)服務(wù)崩潰,導(dǎo)致整個系統(tǒng)無法使用。

          緩存穿透

          定義:緩存穿透是由于客戶端求的數(shù)據(jù)在緩存中不存在,然后去查詢數(shù)據(jù)庫,然而數(shù)據(jù)庫沒有客戶端要查詢的數(shù)據(jù),導(dǎo)致每一次請求都會走數(shù)據(jù)庫查詢操作。真正的問題在于該數(shù)據(jù)本身就是不存在的。

          舉例:客戶端請求商品詳情信息時,攜帶一個商品ID,此時該商品ID是不存在的(不管是緩存中還是數(shù)據(jù)庫中)。導(dǎo)致每一次請求該ID商品的數(shù)據(jù)信息都會走數(shù)據(jù)庫。

          危害:由于請求的參數(shù)對應(yīng)的數(shù)據(jù)根本不存在,會導(dǎo)致每一次都會請求數(shù)據(jù)庫,增加數(shù)據(jù)庫的壓力或者服務(wù)崩潰,更有甚至影響到其他的業(yè)務(wù)模塊。經(jīng)常發(fā)生在用戶惡意請求的情況下會發(fā)生。

          解決方案:

          1. 1. 根據(jù)請求的參數(shù)緩存一個null值。并且為該值設(shè)置一個過期時間,可以將時間設(shè)置短暫一點。

          2. 2. 使用布隆過濾器,首先通過布隆過濾器進(jìn)行篩選,如果在過濾器中存在則去查詢數(shù)據(jù)庫,然后添加到緩存中。如果不存在則直接返回客戶端數(shù)據(jù)不存在。

          3. 3. 由于緩存穿透可能是用戶發(fā)起惡意請求,可以將用戶ip給記錄下來,針對惡意的ip請求進(jìn)行封禁。

          方案分析:

          1. 1. 第一種方案,針對不存在的key,會緩存一個空的值。假設(shè)這樣的請求特別多,是否都會一一去設(shè)置一個空值的緩存,此時Redis中就存在大量無效的緩存空值。假設(shè)這樣的key是商品或者文章類的ID,我們在設(shè)置空值之后,如果后臺添加數(shù)據(jù)應(yīng)該去更新ID對應(yīng)的緩存值,并設(shè)置一個合理的過期時間。

          2. 2. 第二種方案,也是業(yè)界使用最多的一種方案。布隆過濾器的優(yōu)點在于基于Redis實現(xiàn),內(nèi)存操作并且底層的實現(xiàn)也是非常節(jié)約內(nèi)存。關(guān)于布隆過濾器的介紹,可以參考該文章[1]。當(dāng)后臺添加數(shù)據(jù)成功時,將該數(shù)據(jù)的ID添加到布隆過濾器中,前端在請求時先走布隆過濾器進(jìn)行驗證是否存在。但布隆過濾器也存在一個弊端,就是hash沖突問題。這里的hash沖突是什么意思呢?就是說多個ID在進(jìn)行hash計算時,得到的hash位都是同一個值,這就導(dǎo)致在驗證是否存在時誤判。本身是有的,得到的結(jié)果是沒有。布隆過濾器的一個弊端就是,它說有并不一定有,它說沒有就一點是沒有的。

          3. 3. 第三種方案,針對同一用戶一段時間內(nèi)發(fā)起大量的請求,觸發(fā)緩存穿透機(jī)制,此時我們可以顯示該客戶端的訪問。但攻擊者如果是發(fā)起DDOS這樣的攻擊,是沒法完全的避免此類攻擊,因此這種方案不是一個很好的解決方案。

          方案總結(jié):

          1. 1. 我們首先在請求層面增加第3中方案,做一個限流機(jī)制、IP黑名單機(jī)制,控制一些惡意的請求,如果是誤判我們可以實現(xiàn)IP解封這樣的操作。在緩存層則使用第1中方案實現(xiàn)。設(shè)置一個合理的緩存時間。

          2. 2. 對于能容忍誤判的業(yè)務(wù)場景,可以直接才用第2中方案實現(xiàn)。完全基于Redis,減少了系統(tǒng)的復(fù)雜度。

          緩存擊穿

          定義:緩存擊穿是因為某個熱點key不存在,導(dǎo)致走數(shù)據(jù)庫查詢。增加了數(shù)據(jù)庫的壓力。這種壓力可能是瞬間的,也可能是比較持久的。真正的問題在于該key是存在,只是緩存中不存在,導(dǎo)致走數(shù)據(jù)庫操作。

          舉例:有一個熱門的商品,用戶查看商品詳情時攜帶商品的ID以獲取到商品的詳情信息。此時緩存中的數(shù)據(jù)已經(jīng)過期了,因此來的所有請求都要走數(shù)據(jù)庫去查詢。

          危害:相對緩存穿透而言,該數(shù)據(jù)在數(shù)據(jù)庫中是存在的,只是因為緩存過期了,導(dǎo)致要走一次數(shù)據(jù)庫,然后在添加到緩存中,下次請求就能正常走緩存。所謂的危害同樣的還是針對數(shù)據(jù)庫層面的危害。

          解決方案:

          1. 1. 加互斥鎖。針對第一個請求,發(fā)現(xiàn)緩存中沒有數(shù)據(jù),此時查詢數(shù)據(jù)庫添加到緩存里面。這樣后面的請求就不需要走數(shù)據(jù)庫查詢。

          2. 2. 增加業(yè)務(wù)邏輯過期時間。在設(shè)置緩存時,我們可以添加一個緩存過期時間。每次去讀取的時候,做一個判斷,如果這個過期時間與當(dāng)前時間小于一個范圍,觸發(fā)一個后臺線程,去數(shù)據(jù)庫拉取一下數(shù)據(jù),接著更新一下緩存數(shù)據(jù)和緩存的過期時間。其實原理就是代碼層面給緩存延長緩存時長。

          3. 3. 數(shù)據(jù)預(yù)熱。實現(xiàn)通過后臺把數(shù)據(jù)添加到緩存里面。例如秒殺場景開始前,就把商品的庫存添加到緩存里面,這樣用戶請求來了之后,就直接走緩存。

          4. 4. 永久不過期。在給緩存設(shè)置過期時間時,讓它永久不過期。后臺單獨開啟一個線程,來維護(hù)這些緩存的過期時間和數(shù)據(jù)更新。

          方案分析:

          1. 1. 互斥鎖保證了只有一個請求走數(shù)據(jù)庫,這是一個優(yōu)點。但是對于分布式的系統(tǒng),得才用分布式鎖實現(xiàn),分布式鎖的實現(xiàn)本身就有一定的難點,這樣提升了系統(tǒng)的復(fù)雜度。關(guān)于Redis分布式的介紹,可以參考該文章[2]。

          2. 2. 第2種方案,利用Redis不過期,業(yè)務(wù)過期的方案實現(xiàn)。保證了每一次請求都能拿到數(shù)據(jù),同時也可以做到一個后臺線程去更新數(shù)據(jù)。缺點在于后臺線程沒有更新完數(shù)據(jù),此時請求拿到的數(shù)據(jù)是舊數(shù)據(jù),可能對應(yīng)實時性要求高的業(yè)務(wù)場景存在弊端。

          3. 3. 第3種方案,使用緩存預(yù)熱每次加載都走緩存,與第2種方案差不多。不過也存在熱點數(shù)據(jù)更新問題,因此該方案適合數(shù)據(jù)實時性要求不高的數(shù)據(jù)。

          4. 4. 第4中方案,和第2、3種方案類似,在此基礎(chǔ)上進(jìn)行了一定優(yōu)化,使用后臺異步線程主動去更新緩存數(shù)據(jù)。難點在于更新的頻率控制。

          方案總結(jié):

          1. 1. 對于實時性要求高的數(shù)據(jù),推薦使用第1種方案,雖然在技術(shù)上有一定的難度但是能做到數(shù)據(jù)的實時性處理。如果發(fā)生某些請求等待時間久,可以返回異常,讓客戶端重新發(fā)送一次請求。

          2. 2. 對于實時性要求不高的數(shù)據(jù),可以使用第4種方案。

          緩存雪崩

          定義:前面在說到緩存擊穿,是因為緩存中的某個熱點key失效,導(dǎo)致大量請求走數(shù)據(jù)庫。然而緩存雪崩其實也是同樣的道理,只不過這個更嚴(yán)重而已,是大部分緩存的key失效,而不是一個或者兩個key失效。

          舉例:在一個電商系統(tǒng)中,某一個分類下的商品數(shù)據(jù)在緩存中都失效了。然而當(dāng)前系統(tǒng)的很多請求都是該分類下面的商品數(shù)據(jù)。這樣就導(dǎo)致所有的請求都走數(shù)據(jù)庫查詢。

          危害:由于一瞬間大量的請求涌入,每一個請求都要走數(shù)據(jù)庫進(jìn)行查詢。數(shù)據(jù)庫瞬間流量涌入,嚴(yán)重增加數(shù)據(jù)庫負(fù)擔(dān),很容易導(dǎo)致數(shù)據(jù)庫直接癱瘓。

          解決方案:

          1. 1. 緩存時間隨機(jī)。因為某一時間,大量的緩存失效,說明緩存的過期時間比較集中。我們直接將過期的時間設(shè)置為不集中,隨機(jī)打亂。這樣緩存過期時間相對不會很集中,就不會出現(xiàn)同一時刻大量請求走數(shù)據(jù)庫進(jìn)行查詢操作。

          2. 2. 多級緩存。不單純的靠Redis來做緩存,我們也可以使用memcached來做緩存(這里只是舉一個例子,其他的緩存服務(wù)也可以)。緩存數(shù)據(jù)時,對Redis做一個緩存,對memcached做一個緩存。如果Redis失效了,我們可以走memcached。

          3. 3. 互斥鎖。緩存擊穿中我們提到了使用互斥鎖來實現(xiàn),同樣我們也可以用在雪崩的情況下。

          4. 4. 設(shè)置過期標(biāo)志。其實也可以用到緩存擊穿中講到的永久不過期。當(dāng)請求時,判斷過期時間,如果臨近過期時間則設(shè)置一個過期標(biāo)志,觸發(fā)一個獨立的線程去對這個緩存進(jìn)行更新。

          方案分析:

          1. 1. 第1種方案采用隨機(jī)數(shù)緩存時間,能保證key的失效時間分散。難點在于如何設(shè)置緩存時間,如果對于一些需要設(shè)置短緩存時間并數(shù)據(jù)量非常大的數(shù)據(jù),該方案就需要合理的控制時間。

          2. 2. 第2種方案使用多級緩存,可以保證請求全部走緩存數(shù)據(jù)。但這樣增加了系統(tǒng)的架構(gòu)難度,以及其他的各種問題,例如緩存多級更新。

          3. 3. 第3種方案使用互斥鎖,在緩存擊穿中我們提到了互斥鎖,在雪崩的場景中我們雖然能使用,但是這樣會產(chǎn)生大量的分布式鎖。

          4. 4. 第4種方案使用邏輯緩存時間,很好的保證了系統(tǒng)的緩存壓力。

          方案總結(jié):在實際的項目中推薦使用第1、2和4種方案試下會更好一些。

          總結(jié)

          1. 1. 緩存穿透是因為數(shù)據(jù)庫本身沒有該數(shù)據(jù)。

          2. 2. 緩存擊穿和緩存雪崩是數(shù)據(jù)庫中存在該數(shù)據(jù),只是緩存中的數(shù)據(jù)失效了,導(dǎo)致重新要查詢一次數(shù)據(jù)庫再添加到緩存中去。

          3. 3. 緩存擊穿是針對部分熱點key,而緩存雪崩是大面積緩存失效。兩則原理上其實是一樣的,無非就是針對緩存的key的劃分不同而已。

          引用鏈接

          [1]?關(guān)于布隆過濾器的介紹,可以參考該文章:?https://bruce_qiq.gitee.io/mianshi/#/article/redis/article_6
          [2]?關(guān)于Redis分布式的介紹,可以參考該文章:?https://bruce_qiq.gitee.io/mianshi/#/article/redis/article_7


          瀏覽 102
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美午夜理伦三级在线观看 | 青娱乐国产在线视频 | 欧美成人无码一区二区三区 | 69热视频| 插吧插吧综合网 |