天天看點

緩存穿透、緩存并發、緩存失效之思路變遷

我們在用緩存的時候,不管是redis或者memcached,基本上會通用遇到以下三個問題:

緩存穿透

緩存并發

緩存失效

一、緩存穿透

緩存穿透、緩存并發、緩存失效之思路變遷
緩存穿透、緩存并發、緩存失效之思路變遷
緩存穿透、緩存并發、緩存失效之思路變遷

注:

上面三個圖會有什麼問題呢?

我們在項目中使用緩存通常都是先檢查緩存中是否存在,如果存在直接傳回緩存内容,如果不存在就直接查詢資料庫然後再緩存查詢結果傳回。這個時候如果我們查詢的某一個資料在緩存中一直不存在,就會造成每一次請求都查詢db,這樣緩存就失去了意義,在流量大時,可能db就挂掉了。

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

要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。

有一個比較巧妙的作法是,可以将這個不存在的key預先設定一個值。

比如,”key” , “&&”。

在傳回這個&&值的時候,我們的應用就可以認為這是不存在的key,那我們的應用就可以決定是否繼續等待繼續通路,還是放棄掉這次操作。如果繼續等待通路,過一個時間輪詢點後,再次請求這個key,如果取到的值不再是&&,則可以認為這時候key有值了,進而避免了透傳到資料庫,進而把大量的類似請求擋在了緩存之中。

二、緩存并發

有時候如果網站并發通路高,一個緩存如果失效,可能出現多個程序同時查詢db,同時設定緩存的情況,如果并發确實很大,這也可能造成db壓力過大,還有緩存頻繁更新的問題。

我現在的想法是對緩存查詢加鎖,如果key不存在,就加鎖,然後查db入緩存,然後解鎖;其他程序如果發現有鎖就等待,然後等解鎖後傳回資料或者進入db查詢。

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

三、緩存失效

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

那如何解決這些問題呢?

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

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

總結來看:

1、緩存穿透:查詢一個必然不存在的資料。比如文章表,查詢一個不存在的id,每次都會通路db,如果有人惡意破壞,很可能直接對db造成影響。

2、緩存失效:如果緩存集中在一段時間内失效,db的壓力凸顯。這個沒有完美解決辦法,但可以分析使用者行為,盡量讓失效時間點均勻分布。

當發生大量的緩存穿透,例如對某個失效的緩存的大并發通路就造成了緩存雪崩。

四、大家提問彙總

1、問題1:

如何解決db和緩存一緻性問題?

答:當修改了資料庫後,有沒有及時修改緩存。這種問題,以前有過實踐,修改資料庫成功,而修改緩存失敗的情況,最主要就是緩存伺服器挂了。而因為網絡問題引起的沒有及時更新,可以通過重試機制來解決。而緩存伺服器挂了,請求首先自然也就無法到達,進而直接通路到資料庫。那麼我們在修改資料庫後,無法修改緩存,這時候可以将這條資料放到資料庫中,同時啟動一個異步任務定時去檢測緩存伺服器是否連接配接成功,一旦連接配接成功則從資料庫中按順序取出修改資料,依次進行緩存最新值的修改。

2、問題2:

問下緩存穿透那塊!例如,一個使用者查詢文章,通過id查詢,按照之前說的,是将緩存的key預先設定一個值,,如果通過id插過來,發現是預先設定的一個值,比如說是“&&”,那之後的繼續等待通路是什麼意思,這個id什麼時候會真正被附上使用者所需要的值呢?

答:我剛說的主要是咱們常用的後面配置,前台擷取的場景。前台無法擷取相應的key,則等待,或者放棄。當在背景配置界面上配置了相關key和value之後,那麼以前的key &&也自然會被替換掉。你說的那種情況,自然也應該會有一個程序會在某一個時刻,在緩存中設定這個id,再有新的請求到達的時候,就會擷取到最新的id和value。

3、問題3:

其實用redis的話,那天看到一個不錯的例子,雙key,有一個當時生成的一個附屬key來辨別資料修改到期時間,然後快到的時候去重新加載資料,如果覺得key多可以把結束時間放到主key中,附屬key起到鎖的功能。

答:這種方案,之前我們實踐過。這種方案會産生雙份資料,而且需要同時控制附屬key與key之間的關系,操作上有一定複雜度。

4、問題4:

多級緩存是什麼概念呢?