緩存穿透、并發(fā)和失效

我們在用緩存的時候,不管是Redis或者M(jìn)emcached,基本上會通用遇到以下三個問題:

  • 緩存并發(fā)
  • 緩存失效
  • 緩存穿透
image.png
image.png

注:上面三個圖會有什么問題呢?

我們在項(xiàng)目中使用緩存通常都是先檢查緩存中是否存在,如果存在直接返回緩存內(nèi)容,如果不存在就直接查詢數(shù)據(jù)庫然后再緩存查詢結(jié)果返回。這個時候如果我們查詢的某一個數(shù)據(jù)在緩存中一直不存在,就會造成每一次請求都查詢DB,這樣緩存就失去了意義,在流量大時,可能DB就掛掉了。

那這種問題有什么好辦法解決呢?

要是有人利用不存在的key頻繁攻擊我們的應(yīng)用,這就是漏洞。有一個比較巧妙的作法是,可以將這個不存在的key預(yù)先設(shè)定一個值。比如,“key” , “&&”。

在返回這個&&值的時候,我們的應(yīng)用就可以認(rèn)為這是不存在的key,那我們的應(yīng)用就可以決定是否繼續(xù)等待繼續(xù)訪問,還是放棄掉這次操作。如果繼續(xù)等待訪問,過一個時間輪詢點(diǎn)后,再次請求這個key,如果取到的值不再是&&,則可以認(rèn)為這時候key有值了,從而避免了透傳到數(shù)據(jù)庫,從而把大量的類似請求擋在了緩存之中。

緩存并發(fā)

有時候如果網(wǎng)站并發(fā)訪問高,一個緩存如果失效,可能出現(xiàn)多個進(jìn)程同時查詢DB,同時設(shè)置緩存的情況,如果并發(fā)確實(shí)很大,這也可能造成DB壓力過大,還有緩存頻繁更新的問題。
我現(xiàn)在的想法是對緩存查詢加鎖,如果KEY不存在,就加鎖,然后查DB入緩存,然后解鎖;其他進(jìn)程如果發(fā)現(xiàn)有鎖就等待,然后等解鎖后返回?cái)?shù)據(jù)或者進(jìn)入DB查詢。

這種情況和剛才說的預(yù)先設(shè)定值問題有些類似,只不過利用鎖的方式,會造成部分請求等待。

緩存失效

引起這個問題的主要原因還是高并發(fā)的時候,平時我們設(shè)定一個緩存的過期時間時,可能有一些會設(shè)置1分鐘啊,5分鐘這些,并發(fā)很高時可能會出在某一個時間同時生成了很多的緩存,并且過期時間都一樣,這個時候就可能引發(fā)一當(dāng)過期時間到后,這些緩存同時失效,請求全部轉(zhuǎn)發(fā)到DB,DB可能會壓力過重。

那如何解決這些問題呢?

其中的一個簡單方案就時講緩存失效時間分散開,比如我們可以在原有的失效時間基礎(chǔ)上增加一個隨機(jī)值,比如1-5分鐘隨機(jī),這樣每一個緩存的過期時間的重復(fù)率就會降低,就很難引發(fā)集體失效的事件。

我們討論的第二個問題時針對同一個緩存,第三個問題時針對很多緩存。

總結(jié)

1、緩存穿透:查詢一個必然不存在的數(shù)據(jù)。比如文章表,查詢一個不存在的id,每次都會訪問DB,如果有人惡意破壞,很可能直接對DB造成影響。
2、緩存失效:如果緩存集中在一段時間內(nèi)失效,DB的壓力凸顯。這個沒有完美解決辦法,但可以分析用戶行為,盡量讓失效時間點(diǎn)均勻分布。

當(dāng)發(fā)生大量的緩存穿透,例如對某個失效的緩存的大并發(fā)訪問就造成了緩存雪崩。

精彩問答

問題:如何解決DB和緩存一致性問題?
當(dāng)修改了數(shù)據(jù)庫后,有沒有及時修改緩存。這種問題,以前有過實(shí)踐,修改數(shù)據(jù)庫成功,而修改緩存失敗的情況,最主要就是緩存服務(wù)器掛了。而因?yàn)榫W(wǎng)絡(luò)問題引起的沒有及時更新,可以通過重試機(jī)制來解決。而緩存服務(wù)器掛了,請求首先自然也就無法到達(dá),從而直接訪問到數(shù)據(jù)庫。那么我們在修改數(shù)據(jù)庫后,無法修改緩存,這時候可以將這條數(shù)據(jù)放到數(shù)據(jù)庫中,同時啟動一個異步任務(wù)定時去檢測緩存服務(wù)器是否連接成功,一旦連接成功則從數(shù)據(jù)庫中按順序取出修改數(shù)據(jù),依次進(jìn)行緩存最新值的修改。

問題:問下緩存穿透那塊!例如,一個用戶查詢文章,通過ID查詢,按照之前說的,是將緩存的KEY預(yù)先設(shè)置一個值,,如果通過ID插過來,發(fā)現(xiàn)是預(yù)先設(shè)定的一個值,比如說是“&&”,那之后的繼續(xù)等待訪問是什么意思,這個ID什么時候會真正被附上用戶所需要的值呢?
我剛說的主要是咱們常用的后面配置,前臺獲取的場景。前臺無法獲取相應(yīng)的key,則等待,或者放棄。當(dāng)在后臺配置界面上配置了相關(guān)key和value之后,那么以前的key &&也自然會被替換掉。你說的那種情況,自然也應(yīng)該會有一個進(jìn)程會在某一個時刻,在緩存中設(shè)置這個ID,再有新的請求到達(dá)的時候,就會獲取到最新的ID和value。

問題:其實(shí)用Redis的話,那天看到一個不錯的例子,雙key,有一個當(dāng)時生成的一個附屬key來標(biāo)識數(shù)據(jù)修改到期時間,然后快到的時候去重新加載數(shù)據(jù),如果覺得key多可以把結(jié)束時間放到主key中,附屬key起到鎖的功能。
這種方案,之前我們實(shí)踐過。這種方案會產(chǎn)生雙份數(shù)據(jù),而且需要同時控制附屬key與key之間的關(guān)系,操作上有一定復(fù)雜度。

問題:多級緩存是什么概念呢?
多級緩存就像我今天之前給大家發(fā)的文章里面提到了,將Ehcache與Redis做二級緩存,提到過的。但同樣會存在一致性問題,如果我們需要強(qiáng)一致性的話,緩存與數(shù)據(jù)庫同步是會存在時間差的,所以我們在具體開發(fā)的過程中,一定要根據(jù)場景來具體分析,二級緩存更多的解決是,緩存穿透與程序的健壯性,當(dāng)集中式緩存出現(xiàn)問題的時候,我們的應(yīng)用能夠繼續(xù)運(yùn)行。

原文出處:

緩存穿透、并發(fā)和失效,來自一線架構(gòu)師的解決方案
https://community.qingcloud.com/topic/463

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容