分布式技術(shù)原理與實(shí)戰(zhàn)45講--第34講:如何避免緩存穿透、緩存擊穿、緩存雪崩
設(shè)計(jì)緩存系統(tǒng)不得不考慮的問題是緩存穿透、緩存擊穿與失效時(shí)的雪崩效應(yīng),同時(shí),關(guān)于這幾種問題場(chǎng)景的認(rèn)識(shí)及解決方案,也是面試中的高頻考點(diǎn)。今天的內(nèi)容,可以說(shuō)是緩存應(yīng)用的三板斧,下面我們一起來(lái)分析一下緩存應(yīng)用中的這幾個(gè)熱門問題。
緩存穿透
先來(lái)看一下緩存穿透,顧名思義,是指業(yè)務(wù)請(qǐng)求穿過了緩存層,落到持久化存儲(chǔ)上。在大多數(shù)場(chǎng)景下,我們應(yīng)用緩存是為了承載前端業(yè)務(wù)請(qǐng)求,緩存被擊穿以后,如果請(qǐng)求量比較大,則會(huì)導(dǎo)致數(shù)據(jù)庫(kù)出現(xiàn)風(fēng)險(xiǎn)。

以雙十一為例,由于各類促銷活動(dòng)的疊加,整體網(wǎng)站的訪問量、商品曝光量會(huì)是平時(shí)的千倍甚至萬(wàn)倍。巨大的流量暴漲,單靠數(shù)據(jù)庫(kù)是不能承載的,如果緩存不能很好的工作,可能會(huì)影響數(shù)據(jù)庫(kù)的穩(wěn)定性,繼而直接影響整體服務(wù)。
那么哪些場(chǎng)景下會(huì)發(fā)生 緩存穿透 呢?
不合理的緩存失效策略
緩存失效策略如果設(shè)置不合理,比如設(shè)置了大量緩存在同一時(shí)間點(diǎn)失效,那么將導(dǎo)致大量緩存數(shù)據(jù)在同一時(shí)刻發(fā)生緩存穿透,業(yè)務(wù)請(qǐng)求直接打到持久化存儲(chǔ)層。
外部用戶的惡意攻擊
外部惡意用戶利用不存在的 Key,來(lái)構(gòu)造大批量不存在的數(shù)據(jù)請(qǐng)求我們的服務(wù),由于緩存中并不存在這些數(shù)據(jù),因此海量請(qǐng)求全部穿過緩存,落在數(shù)據(jù)庫(kù)中,將導(dǎo)致數(shù)據(jù)庫(kù)崩潰。
介紹了出現(xiàn)緩存穿透的原因,那么緩存穿透如何在業(yè)務(wù)中避免呢?首先是設(shè)置合理的緩存失效策略,避免緩存數(shù)據(jù)在同一時(shí)間失效。對(duì)于緩存失效策略的討論,將在后面的第 36 課時(shí)中進(jìn)行介紹,這里暫時(shí)不展開。
緩存穿透還可以通過緩存空數(shù)據(jù)的方式避免。緩存空數(shù)據(jù)非常好理解,就是針對(duì)數(shù)據(jù)庫(kù)不存在的數(shù)據(jù),在查詢?yōu)榭諘r(shí),添加一個(gè)對(duì)應(yīng) null 的值到緩存中,這樣在下次請(qǐng)求時(shí),可以通過緩存的結(jié)果判斷數(shù)據(jù)庫(kù)中是否存在,避免反復(fù)的請(qǐng)求數(shù)據(jù)庫(kù)。不過這種方式,需要考慮空數(shù)據(jù)的 Key 在新增后的處理,感興趣的同學(xué)可以思考一下。
另外一個(gè)方案是使用布隆過濾器。布隆過濾器是應(yīng)用非常廣泛的一種數(shù)據(jù)結(jié)構(gòu),我們熟悉的 Bitmap,可以看作是一種特殊的布隆過濾器,布隆過濾器的實(shí)現(xiàn)細(xì)節(jié)不是本課時(shí)關(guān)注的重點(diǎn),如果你對(duì)布隆過濾器還不熟悉,可以抽空查閱數(shù)據(jù)結(jié)構(gòu)相關(guān)的資料學(xué)習(xí)。
使用布隆過濾器,可在緩存前添加一層過濾,布隆過濾器映射到緩存,在緩存中不存在的數(shù)據(jù),會(huì)在布隆過濾器這一層攔截,從而保護(hù)緩存和數(shù)據(jù)庫(kù)的安全。

緩存擊穿
緩存擊穿也是緩存應(yīng)用常見的問題場(chǎng)景,其是一個(gè)非常形象的表達(dá)。具體表現(xiàn):前端請(qǐng)求大量的訪問某個(gè)熱點(diǎn) Key,而這個(gè)熱點(diǎn) Key 在某個(gè)時(shí)刻恰好失效,導(dǎo)致請(qǐng)求全部落到數(shù)據(jù)庫(kù)上。
不知道你有沒有聽過二八定律(80/20 定律、帕累托法則),百度百科中對(duì)二八定律的具體描述是這樣的:
在任何一組東西中,最重要的只占其中一小部分,約 20%,其余 80% 盡管是多數(shù),卻是次要的,因此又稱二八定律。
二八定律在緩存應(yīng)用中也不能避免,往往是 20% 的緩存數(shù)據(jù),承擔(dān)了 80% 或者更高的請(qǐng)求,剩下 80% 的緩存數(shù)據(jù),僅僅承擔(dān)了 20% 的訪問流量。
由于二八定律的存在,緩存擊穿雖然可能只是一小部分?jǐn)?shù)據(jù)失效,但這部分?jǐn)?shù)據(jù)如果恰好是熱點(diǎn)數(shù)據(jù),還是會(huì)對(duì)系統(tǒng)造成非常大的危險(xiǎn)。
緩存擊穿和緩存穿透都是降低了整體的緩存命中率,不過在表現(xiàn)上比較類似。緩存擊穿可以認(rèn)為是緩存穿透的一種特殊場(chǎng)景,所以在解決方案上也可以應(yīng)用上面提到的那幾種手段。
接下來(lái)看一下緩存雪崩,其是緩存穿透和緩存擊穿升級(jí)的一種問題場(chǎng)景。
緩存雪崩
緩存雪崩的表現(xiàn)有兩種,一種是大量的緩存數(shù)據(jù)在同一時(shí)刻失效,請(qǐng)求全部轉(zhuǎn)發(fā)到數(shù)據(jù)庫(kù),將導(dǎo)致數(shù)據(jù)庫(kù)壓力過大,服務(wù)宕機(jī);另外一種是緩存服務(wù)不穩(wěn)定,比如負(fù)責(zé)緩存的 Redis 集群宕機(jī)。
在業(yè)務(wù)開發(fā)中,出現(xiàn)緩存雪崩非常危險(xiǎn),可能會(huì)直接導(dǎo)致大規(guī)模服務(wù)不可用,因?yàn)榫彺媸r(shí)導(dǎo)致的雪崩,一方面是整體的數(shù)據(jù)存儲(chǔ)鏈路,另一方面是服務(wù)調(diào)用鏈路,最終導(dǎo)致微服務(wù)整體的對(duì)外服務(wù)出現(xiàn)問題。
我們知道,微服務(wù)本身就存在雪崩效應(yīng),在電商場(chǎng)景中,如果商品服務(wù)不可用,最終可能會(huì)導(dǎo)致依賴的訂單服務(wù)、購(gòu)物車服務(wù)、用戶瀏覽等級(jí)聯(lián)出現(xiàn)故障。
你考慮一下,如果商品服務(wù)出現(xiàn)緩存雪崩,繼而商品服務(wù)不可用,關(guān)聯(lián)的周邊服務(wù)都會(huì)受影響。

那么緩存雪崩在業(yè)務(wù)中如何避免呢?
首先是明確緩存集群的容量峰值,通過合理的限流和降級(jí),防止大量請(qǐng)求直接拖垮緩存;其次是做好緩存集群的高可用,以 Redis 為例,可以通過部署 RedisCluster、Proxy 等不同的緩存集群,來(lái)實(shí)現(xiàn)緩存集群高可用。
緩存穩(wěn)定性
今天介紹的內(nèi)容,是大家在緩存應(yīng)用時(shí)的一些總結(jié),現(xiàn)在我希望你跳出這幾個(gè)名詞,從一個(gè)更高的維度來(lái)思考緩存應(yīng)用的穩(wěn)定性。
首先明確應(yīng)用緩存的目的,大部分緩存都是內(nèi)存數(shù)據(jù)庫(kù),并且可以支持非常高的 QPS,所以緩存應(yīng)用,可以防止海量業(yè)務(wù)請(qǐng)求擊垮數(shù)據(jù)庫(kù),保護(hù)正常的服務(wù)運(yùn)行。
其次,在考慮緩存的穩(wěn)定性時(shí),要從兩個(gè)方面展開,第一個(gè)是緩存的數(shù)據(jù),第二個(gè)是緩存容器也就是緩存服務(wù)本身的穩(wěn)定性。
從緩存數(shù)據(jù)的層面,有一個(gè)緩存命中率的概念,是指落到緩存上的請(qǐng)求占整體請(qǐng)求總量的占比。緩存命中率在電商大促等場(chǎng)景中是一個(gè)非常關(guān)鍵的指標(biāo),我們要盡可能地提高緩存數(shù)據(jù)的命中率,一般要求達(dá)到 90% 以上,如果是大促等場(chǎng)景,會(huì)要求 99% 以上的命中率。
從緩存服務(wù)的層面,緩存集群本身也是一個(gè)服務(wù),也會(huì)有集群部署,服務(wù)可用率,服務(wù)的最大容量等。在應(yīng)用緩存時(shí),要對(duì)緩存服務(wù)進(jìn)行壓測(cè),明確緩存的最大水位,如果當(dāng)前系統(tǒng)容量超過緩存閾值,就要通過其他的高可用手段來(lái)進(jìn)行調(diào)整,比如服務(wù)限流,請(qǐng)求降級(jí),使用消息隊(duì)列等不同的方式。
總結(jié)
這一課時(shí)的內(nèi)容,分享了分布式緩存應(yīng)用和面試的經(jīng)典問題:緩存穿透、緩存擊穿、緩存雪崩,以及對(duì)應(yīng)這幾種業(yè)務(wù)場(chǎng)景的解決方案。今天介紹的這幾個(gè)問題場(chǎng)景,只是對(duì)緩存應(yīng)用時(shí)一些高頻問題的抽象,在實(shí)際業(yè)務(wù)開發(fā)中,永遠(yuǎn)都是具體情況具體分析,對(duì)不同的業(yè)務(wù),適用不同的解決方案。
你在應(yīng)用緩存時(shí)有哪些心得體會(huì),比如如何提高緩存命中率,如何處理熱點(diǎn) Key 等,歡迎留言進(jìn)行分享。