一篇吃透Redis緩存穿透、雪崩、擊穿問(wèn)題
前言:在學(xué)Redis之前我們查詢數(shù)據(jù)的時(shí)候都是直接查詢數(shù)據(jù)庫(kù)的,但是這樣會(huì)有一個(gè)潛在的問(wèn)題:“如果用戶量很大,所有請(qǐng)求都去訪問(wèn)數(shù)據(jù)庫(kù),那么會(huì)使數(shù)據(jù)庫(kù)壓力過(guò)大,導(dǎo)致性能下降甚至宕機(jī)”。因此,我們需要把經(jīng)常訪問(wèn)的數(shù)據(jù)放到緩存中,這里我們用Redis作為緩存。
但是,使用Redis作為緩存的過(guò)程中我們一般如下,如下圖:我們查詢某個(gè)數(shù)據(jù),前端發(fā)送請(qǐng)求到后端,后端根據(jù)請(qǐng)求去查詢數(shù)據(jù),一開(kāi)始先去Redis中查有無(wú)這個(gè)數(shù)據(jù),如果有則直接返回,沒(méi)有則去數(shù)據(jù)庫(kù)中查找并且將查找到的結(jié)果返回,如果數(shù)據(jù)庫(kù)中沒(méi)有則返回錯(cuò)誤信息。
那么,在使用Redis作為緩存的過(guò)程中我們避免不了會(huì)遇到以下幾個(gè)常見(jiàn)的問(wèn)題:①Redis與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性問(wèn)題。②緩存穿透。③緩存雪崩。④緩存擊穿。
我們先來(lái)說(shuō)①緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)庫(kù)一致性問(wèn)題。
對(duì)于Redis作為緩存我們有以下用法:①只讀緩存。②讀寫(xiě)緩存。
只讀緩存是指對(duì)于修改數(shù)據(jù)的時(shí)候,我們只對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改,同時(shí)刪除緩存,這樣我們永遠(yuǎn)能保證數(shù)據(jù)庫(kù)中有最新的數(shù)據(jù),但是這樣每次修改數(shù)據(jù)的時(shí)候,我們查找該數(shù)據(jù)的時(shí)候都需要查找一次數(shù)據(jù)庫(kù),因此性能會(huì)有所降低。
讀寫(xiě)緩存是指我們修改數(shù)據(jù)的時(shí)候?qū)彺娴臄?shù)據(jù)也直接進(jìn)行修改,那么又可以分為兩種:①同步讀寫(xiě)②異步讀寫(xiě)。
同步讀寫(xiě)是指修改數(shù)據(jù)的時(shí)候同時(shí)修改緩存和數(shù)據(jù)庫(kù),并且通過(guò)事務(wù)的特性保證二者數(shù)據(jù)一致性,但是由于緩存的速度遠(yuǎn)快于數(shù)據(jù)庫(kù)的速度,因此性能還是會(huì)有一定的限制。
異步讀寫(xiě)是指我們每次修改數(shù)據(jù)都是在緩存中修改,每隔一段時(shí)間將緩存中的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)中,這樣每次修改的時(shí)間只是修改緩存數(shù)據(jù)的時(shí)間,性能大大提高。但是這樣會(huì)存在一個(gè)隱患:“當(dāng)Redis突發(fā)情況宕機(jī)的時(shí)候,數(shù)據(jù)還沒(méi)來(lái)得及導(dǎo)入數(shù)據(jù)庫(kù),那么這段時(shí)間還沒(méi)保存進(jìn)數(shù)據(jù)庫(kù)的數(shù)據(jù)就會(huì)丟失”,因此一致性無(wú)法保證。
然后我們來(lái)說(shuō)說(shuō)②緩存穿透。由我們Redis作為緩存的流程圖可以知道:我們每次查詢數(shù)據(jù)的時(shí)候都是先查詢緩存,查詢不到再查數(shù)據(jù)庫(kù),如果數(shù)據(jù)庫(kù)中也查詢不到則返回錯(cuò)誤信息。
那么,對(duì)于一個(gè)查詢不到的數(shù)據(jù),如果有心懷不軌的人,寫(xiě)一個(gè)程序,多線程并發(fā)的無(wú)限查詢這個(gè)數(shù)據(jù),那么我們每次都需要訪問(wèn)數(shù)據(jù)庫(kù),會(huì)導(dǎo)致數(shù)據(jù)庫(kù)性能下降甚至宕機(jī),這就是緩存穿透。
那么,我們?nèi)绾蝸?lái)解決緩存穿透呢?這里我們說(shuō)兩種方法:
方法一:Redis緩存中存儲(chǔ)空值。我們可以知道,緩存穿透是由于某些不存在的數(shù)據(jù),每次查詢,我們?cè)诰彺嬷卸疾檎也坏皆摂?shù)據(jù),因此每次都需要去訪問(wèn)數(shù)據(jù)庫(kù)。那么我們可以不可以對(duì)于這些數(shù)據(jù)也存入緩存中呢?這樣我們每次查找這些數(shù)據(jù),就只會(huì)第一次查找數(shù)據(jù)庫(kù),后面都是查找緩存了。那么有人會(huì)問(wèn)了?“咦,不存在的數(shù)據(jù)怎么查找呢?”所以我們這邊是將某個(gè)不存在的數(shù)據(jù)存儲(chǔ)在緩存中,并且存儲(chǔ)的值為空字符串(也可以是其他的,這里取決于你與前端兄弟的約定)。如下圖:
因此,這樣我們對(duì)于這些惡意數(shù)據(jù),就只能“騷擾”我們的寶貝數(shù)據(jù)庫(kù)一次了,便解決了緩存穿透的問(wèn)題,那么還有一個(gè)疑問(wèn)就是:如果將來(lái)這些數(shù)據(jù)有了,但是每次查詢的時(shí)候緩存都直接返回空不是嘛?不會(huì)的,因?yàn)槲覀円院髮?duì)于數(shù)據(jù)庫(kù)插入數(shù)據(jù)的同時(shí)會(huì)對(duì)Redis緩存進(jìn)行一個(gè)Set,這個(gè)Set數(shù)據(jù)的操作會(huì)直接覆蓋原有不存在的數(shù)據(jù),因此并不會(huì)出現(xiàn)這種問(wèn)題。
方法二:布隆過(guò)濾器。大致就是在前端頁(yè)面和緩存直接多了一個(gè)布隆過(guò)濾器
布隆過(guò)濾器(Bloom Filter)是1970年由布隆提出的。它實(shí)際上是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)。布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。它的優(yōu)點(diǎn)是空間效率和查詢時(shí)間都比一般的算法要好的多,缺點(diǎn)是有一定的誤識(shí)別率和刪除困難。布隆過(guò)濾器可以告訴我們 “某樣?xùn)|西一定不存在或者可能存在”,也就是說(shuō)布隆過(guò)濾器說(shuō)這個(gè)數(shù)不存在則一定不存,布隆過(guò)濾器說(shuō)這個(gè)數(shù)存在可能不存在。
③緩存雪崩,什么是緩存雪崩呢?緩存雪崩指的是在同一時(shí)間大量Redis緩存中存儲(chǔ)的key過(guò)期或者Redis服務(wù)器直接宕機(jī),并且大量的請(qǐng)求查詢這些數(shù)據(jù)的時(shí)候,會(huì)導(dǎo)致大量請(qǐng)求一窩蜂的涌向數(shù)據(jù)庫(kù)進(jìn)行查詢,導(dǎo)致數(shù)據(jù)庫(kù)壓力過(guò)大,性能下降甚至宕機(jī)。那么如何解決緩存雪崩呢?
對(duì)于情況一:大量key同時(shí)過(guò)期,這里說(shuō)幾種解決方法:
①設(shè)置緩存key過(guò)期時(shí)間可以隨機(jī)設(shè)置,這樣不會(huì)使得大量的key同一時(shí)間段內(nèi)過(guò)期。
②服務(wù)降級(jí):指的是對(duì)于一些非核心數(shù)據(jù)來(lái)說(shuō)(比如查詢一些無(wú)關(guān)緊要的數(shù)據(jù)時(shí))我們可以預(yù)先設(shè)置一些值,當(dāng)無(wú)法訪問(wèn)緩存的時(shí)候,這些數(shù)據(jù)不會(huì)直接查詢數(shù)據(jù)庫(kù),而是返回預(yù)先設(shè)置的值,比如一些錯(cuò)誤信息。
對(duì)于情況二:Redis服務(wù)器直接宕機(jī),這里說(shuō)幾種解決方法:
①搭建Redis服務(wù)集群:嘗試構(gòu)建 Redis 的高可用集群,比如當(dāng)某主節(jié)點(diǎn)掛掉了,集群能夠馬上重新選出新的主節(jié)點(diǎn)。
②業(yè)務(wù)中實(shí)現(xiàn)服務(wù)熔斷或者請(qǐng)求限流機(jī)制:
服務(wù)熔斷:如果監(jiān)聽(tīng)到發(fā)生了緩存雪崩,直接暫停對(duì)緩存服務(wù)的請(qǐng)求,但是這種對(duì)業(yè)務(wù)的影響比較大;
服務(wù)限流:可以在入口做限流,不要讓所有的請(qǐng)求都流入到后端的服務(wù)中;
④緩存擊穿:緩存雪崩指的是大量數(shù)據(jù)無(wú)法從Redis查詢到,而同時(shí)去查詢數(shù)據(jù)庫(kù)導(dǎo)致,緩存擊穿則是某些熱點(diǎn)key,比如雙十一搶蘋(píng)果手機(jī),如果突然間Redis緩存對(duì)于這個(gè)數(shù)據(jù)過(guò)期了,那么這一瞬間大量搶蘋(píng)果手機(jī)的請(qǐng)求都會(huì)去訪問(wèn)數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)性能下降甚至宕機(jī)這里我們講兩種解決方法:①Redis互斥鎖。②緩存數(shù)據(jù)邏輯過(guò)期。
方法一:Redis互斥鎖。以上我們知道:對(duì)于某個(gè)熱點(diǎn)key失效的時(shí)候,由于大量查詢?cè)摂?shù)據(jù)的請(qǐng)求在緩存中查找不到,因此同時(shí)查找數(shù)據(jù)庫(kù)導(dǎo)致。那么我們只需要對(duì)于每個(gè)數(shù)據(jù)設(shè)置一個(gè)互斥鎖,當(dāng)在訪問(wèn)不到緩存的時(shí)候,只有一個(gè)線程能夠去訪問(wèn)數(shù)據(jù)庫(kù),其他線程等待,
- 這樣就解決了以上的問(wèn)題。
但是這樣會(huì)導(dǎo)致一個(gè)問(wèn)題:那就是由于其他線程都要等待,會(huì)導(dǎo)致性能下降,要等那個(gè)拿到互斥鎖的線程查詢完數(shù)據(jù)庫(kù),并且返回?cái)?shù)據(jù)到緩存中才能查到數(shù)據(jù)。
方法二:緩存的數(shù)據(jù)邏輯過(guò)期。我們知道,緩存擊穿是由于熱點(diǎn)key過(guò)期導(dǎo)致去查詢數(shù)據(jù)庫(kù),那么我們可以這樣想:如果這些熱點(diǎn)key只是邏輯過(guò)期(邏輯過(guò)期指雖然過(guò)期了,但是還是在緩存里面不會(huì)刪除,但是程序會(huì)知道是已過(guò)期的數(shù)據(jù),會(huì)訪問(wèn)一次數(shù)據(jù)庫(kù)進(jìn)行更新),那么不就解決了嗎?因此邏輯過(guò)期的解決思路是:對(duì)于這些熱點(diǎn)key,先查詢緩存,如果沒(méi)過(guò)期則直接返回,如果過(guò)期了則開(kāi)一個(gè)新的線程
去查詢數(shù)據(jù)庫(kù),而查詢數(shù)據(jù)庫(kù)過(guò)程中訪問(wèn)緩存的請(qǐng)求直接返回舊數(shù)據(jù)即可。
但是邏輯過(guò)期由于要多一個(gè)線程,因此有一定的內(nèi)存消耗,并且更新過(guò)程中訪問(wèn)的請(qǐng)求都是收到舊的數(shù)據(jù),因此一致性有一定的下降。
以上是使用Redis緩存過(guò)程中的一些常見(jiàn)問(wèn)題,后續(xù)會(huì)慢慢補(bǔ)充。
以上就是一篇吃透Redis緩存穿透、雪崩、擊穿問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Redis緩存穿透、雪崩、擊穿的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Redis數(shù)據(jù)庫(kù)的數(shù)據(jù)傾斜詳解
Redis,英文全稱是Remote Dictionary Server(遠(yuǎn)程字典服務(wù)),是一個(gè)開(kāi)源的使用ANSI C語(yǔ)言編寫(xiě)、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫(kù),需要的朋友可以參考下2023-07-07Redis教程(二):String數(shù)據(jù)類(lèi)型
這篇文章主要介紹了Redis教程(二):String數(shù)據(jù)類(lèi)型,本文講解了String數(shù)據(jù)類(lèi)型概述、相關(guān)命令列表、命令使用示例三部分內(nèi)容,需要的朋友可以參考下2015-04-04Redis發(fā)布訂閱和實(shí)現(xiàn).NET客戶端詳解
發(fā)布訂閱在應(yīng)用級(jí)其作用是為了減少依賴關(guān)系,通常也叫觀察者模式。主要是把耦合點(diǎn)單獨(dú)抽離出來(lái)作為第三方,隔離易變化的發(fā)送方和接收方。下面這篇文章主要給大家介紹了關(guān)于Redis發(fā)布訂閱和實(shí)現(xiàn).NET客戶端的相關(guān)資料,需要的朋友可以參考下2017-03-03redis中scan命令的基本實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于redis中scan命令的基本實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10詳解Redis單線程架構(gòu)的優(yōu)勢(shì)與不足
很多人都遇到過(guò)這么一道面試題:Redis是單線程還是多線程?這個(gè)問(wèn)題既簡(jiǎn)單又復(fù)雜,說(shuō)他簡(jiǎn)單是因?yàn)榇蠖鄶?shù)人都知道Redis是單線程,說(shuō)復(fù)雜是因?yàn)檫@個(gè)答案其實(shí)并不準(zhǔn)確,本文就給大家講講Redis單線程架構(gòu)的優(yōu)勢(shì)與不足,需要的朋友可以參考下2024-02-02