面試官:講講什么是緩存穿透?擊穿?雪崩?如何解決?
作者個人研發(fā)的在高并發(fā)場景下,提供的簡單、穩(wěn)定、可擴展的延遲消息隊列框架,具有精準(zhǔn)的定時任務(wù)和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業(yè)提供了精準(zhǔn)定時調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗。為使更多童鞋受益,現(xiàn)給出開源框架地址:
https://github.com/sunshinelyz/mykit-delay
寫在前面
說到Redis,往往更多的場景是被用作系統(tǒng)的緩存,說到緩存,尤其是分布式緩存系統(tǒng),在實際高并發(fā)場景下,稍有不慎,就會造成緩存穿透、緩存擊穿和緩存雪崩的問題。那什么是緩存穿透?什么是緩存擊穿,又什么是緩存雪崩呢?它們是如何造成的?又該如何解決呢?今天,我們就一起來探討這些問題。
緩存穿透
首先,我們來說說緩存穿透。什么是緩存穿透呢?緩存穿透問題在一定程度上與緩存命中率有關(guān)。如果我們的緩存設(shè)計的不合理,緩存的命中率非常低,那么,數(shù)據(jù)訪問的絕大部分壓力都會集中在后端數(shù)據(jù)庫層面。
什么是緩存穿透?
如果在請求數(shù)據(jù)時,在緩存層和數(shù)據(jù)庫層都沒有找到符合條件的數(shù)據(jù),也就是說,在緩存層和數(shù)據(jù)庫層都沒有命中數(shù)據(jù),那么,這種情況就叫作緩存穿透。
我們可以使用下圖來表示緩存穿透的現(xiàn)象。
造成緩存穿透的主要原因就是:查詢某個Key對應(yīng)的數(shù)據(jù),Redis緩存中沒有相應(yīng)的數(shù)據(jù),則直接到數(shù)據(jù)庫中查詢。數(shù)據(jù)庫中也不存在要查詢的數(shù)據(jù),則數(shù)據(jù)庫會返回空,而Redis也不會緩存這個空結(jié)果。這就造成每次通過這樣的Key去查詢數(shù)據(jù)都會直接到數(shù)據(jù)庫中查詢,Redis不會緩存空結(jié)果。這就造成了緩存穿透的問題。
如何解決緩存穿透問題?
既然我們知道了造成緩存穿透的主要原因就是緩存中不存在相應(yīng)的數(shù)據(jù),直接到數(shù)據(jù)庫查詢,數(shù)據(jù)庫返回空結(jié)果,緩存中不存儲空結(jié)果。
那我們就自然而然的想到了第一種解決方案:就是把空對象緩存起來。當(dāng)?shù)谝淮螐臄?shù)據(jù)庫中查詢出來的結(jié)果為空時,我們就將這個空對象加載到緩存,并設(shè)置合理的過期時間,這樣,就能夠在一定程度上保障后端數(shù)據(jù)庫的安全。
第二種解決緩存穿透問題的解決方案:就是使用布隆過濾器,布隆過濾器可以針對大數(shù)據(jù)量的、有規(guī)律的鍵值進行處理。一條記錄是不是存在,本質(zhì)上是一個Bool值,只需要使用 1bit 就可以存儲。我們可以使用布隆過濾器將這種表示是、否等操作,壓縮到一個數(shù)據(jù)結(jié)構(gòu)中。比如,我們最熟悉的用戶性別這種數(shù)據(jù),就非常適合使用布隆過濾器來處理。
緩存擊穿如果我們?yōu)榫彺嬷械拇蟛糠謹(jǐn)?shù)據(jù)設(shè)置了相同的過期時間,則到了某一時刻,緩存中的數(shù)據(jù)就會批量過期。
什么是緩存擊穿?
如果緩存中的數(shù)據(jù)在某個時刻批量過期,導(dǎo)致大部分用戶的請求都會直接落在數(shù)據(jù)庫上,這種現(xiàn)象就叫作緩存擊穿。
我們可以使用下圖來表示緩存擊穿的線程。
造成緩存擊穿的主要原因就是:我們?yōu)榫彺嬷械臄?shù)據(jù)設(shè)置了過期時間。如果在某個時刻從數(shù)據(jù)庫獲取了大量的數(shù)據(jù),并設(shè)置了相同的過期時間,這些緩存的數(shù)據(jù)就會在同一時刻失效,造成緩存擊穿問題。
如何解決緩存擊穿問題?
對于比較熱點的數(shù)據(jù),我們可以在緩存中設(shè)置這些數(shù)據(jù)永不過期;也可以在訪問數(shù)據(jù)的時候,在緩存中更新這些數(shù)據(jù)的過期時間;如果是批量入庫的緩存項,我們可以為這些緩存項分配比較合理的過期時間,避免同一時刻失效。
還有一種解決方案就是:使用分布式鎖,保證對于每個Key同時只有一個線程去查詢后端的服務(wù),某個線程在查詢后端服務(wù)的同時,其他線程沒有獲得分布式鎖的權(quán)限,需要進行等待。不過在高并發(fā)場景下,這種解決方案對于分布式鎖的訪問壓力比較大。
緩存雪崩
如果緩存系統(tǒng)出現(xiàn)故障,所有的并發(fā)流量就會直接到達數(shù)據(jù)庫。
什么是緩存雪崩?
如果在某一時刻緩存集中失效,或者緩存系統(tǒng)出現(xiàn)故障,所有的并發(fā)流量就會直接到達數(shù)據(jù)庫。數(shù)據(jù)存儲層的調(diào)用量就會暴增,用不了多長時間,數(shù)據(jù)庫就會被大流量壓垮,這種級聯(lián)式的服務(wù)故障,就叫作緩存雪崩。
我們可以用下圖來表示緩存雪崩的現(xiàn)象。
造成緩存雪崩的主要原因就是緩存集中失效,或者緩存服務(wù)發(fā)生故障,瞬間的大并發(fā)流量壓垮了數(shù)據(jù)庫。
如何解決緩存雪崩問題?
解決緩存雪崩問題最常用的一種方案就是保證Redis的高可用,將Redis緩存部署成高可用集群(必要時候做成異地多活),可以有效的防止緩存雪崩問題的發(fā)生。
為了緩解大并發(fā)流量,我們也可以使用限流降級的方式防止緩存雪崩。例如,在緩存失效后,通過加鎖或者使用隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。具體點就是設(shè)置某些Key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。則能夠有效的緩解大并發(fā)流量對數(shù)據(jù)庫帶來的巨大沖擊。
另外,我們也可以通過數(shù)據(jù)預(yù)熱的方式將可能大量訪問的數(shù)據(jù)加載到緩存,在即將發(fā)生大并發(fā)訪問的時候,提前手動觸發(fā)加載不同的數(shù)據(jù)到緩存中,并為數(shù)據(jù)設(shè)置不同的過期時間,讓緩存失效的時間點盡量均勻,不至于在同一時刻全部失效。
本文轉(zhuǎn)載自微信公眾號「冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系冰河技術(shù)公眾號。