緩存讀寫策略 Cache Aside Pattern,開發必備

我們在前面講到了當我們業務面臨大量寫併發的時候,將數據庫開發成分佈式存儲系統,然後又介紹了NoSql數據庫與關係型數據庫互相配合,以用來更好的服務與我們的業務發展。但隨着併發的持續增加,存儲數據量的增多,數據庫的磁盤 IO 逐漸成了系統的瓶頸,我們需要一種訪問更快的組件來降低請求響應時間,提升整體系統性能,這時我們就會使用到緩存。

至於緩存這個概念,這裏就不去多說了,我相信大家都懂,也知道它的作用是爲了讓對數據的請求更快的返回。我們今天要說的就是,應該怎麼去使用緩存,通過學習如何選擇它的讀寫策略來應用到我們開發中。

大家是不是覺得緩存讀寫應該很簡單啊,我先去讀緩存,讀到了就返回數據,讀不到我就去數據庫讀,然後再寫到緩存中去,沒必要專門的講解啊。其實當我們真正面臨緩存讀寫的時候,是不一定的,是要根據我們業務去調整我們策略的,而且需要考慮各種因素的,不能這麼簡單粗暴。那接下來,我們就來看看我們開發中會z怎麼做緩存的讀寫策略。

 

Cache Aside Pattern

Cache Aside 即爲旁路緩存方案,被分爲讀請求和寫請求,下面我們來結合案例看看該怎麼去使用。

 

案例引入

現在我們有一個酒店信息表(爲了大家更好的理解Cache Aside,這裏不引入複雜的房型那些參數),裏面有字段hotel_id,價格字段price等,緩存中按照hotel_id爲key存儲了這個酒店的價格(10001:100),現在的需求是要將hotel_id爲10001的價格改爲200,該怎麼去做呢?

你可能會說,這簡單啊,我就先更新數據庫,然後更新緩存嘛,就是下面圖示這個樣子的:

這樣做是會造數據庫和緩存數據不一致的,假設,現在有個請求 A 過來,將數據庫的hotel_id爲10001的價格改爲200了,如此同時,請求B 也來了將價格改爲300,然後再更新緩存中價格爲300;這個時候請求 A纔來更新價格,將key爲10001的價格改爲了200。這個時候,緩存中價格是200,而數據庫中是300,所以就造成了兩者的數據不一致。因爲,這個更新數據庫和更新緩存是兩個獨立的操作,又沒有采用併發控制,所以他們執行的順序我們是不能保證的,而且這種情況發生的頻率還是很高的。

還有個問題就是會造成數據更新丟失的問題,比如,我們在APP中的積分是100,預定了一間酒店,然後又同時還買了啥保險之類的,這個時候,請求A過來查詢緩存是100然後加5積分,再還沒寫緩存之前,請求B也來了查到的緩存也是100,它同樣是來加5積分的,這個時候,就是緩存更新之後就是105積分,而實際上我們的意願是要增加10積分,就是緩存中得110積分纔對。

 

那我們該怎麼辦?

其實解決上面那樣的問題也很簡單,我們就在更新緩存的時候,不採取更新的方案,採取刪除緩存的方案。即我們在讀取數據的時候,如果緩存沒命中,就去查數據庫,然後再回填到緩存中。

那這種解決方案就是我們的Cache Aside Pattern 旁路緩存策略,它是以數據庫的數據爲基準的,而緩存是按需才加載,一般被分爲讀策略和寫策略。下面我們再來總結下,它的讀寫策略步驟:

讀策略步驟:

  1. 先從緩存中讀取數據

  2. 命中緩存,直接返回

  3. 未命中緩存,則去查數據庫

  4. 查到數據庫的數據再去寫緩存

寫策略步驟:

  1. 先更新數據庫中數據

  2. 再刪除緩存記錄

其實像Cache Aside這種緩存策略,也是有缺點的即也會出現數據不一致的情況,但是概率極低,是可以接受的。

Cache Aside策略,在我們平時開發中,應用的最爲廣泛,但是我們也不能不考慮業務特殊情況,不管三七二十一的一直使用這種讀寫策略。比如,現在我們有這樣的一個需求,我們的酒店後臺系統錄入了一個新的政策,一錄完然後就需要立馬給用戶展示出來,如果採用先更新數據庫,再刪除緩存的話,我們數據讀寫分離會存在延時的,就會存在讀取不到數據的情況。

那這個時候,我們需要怎麼做呢?那我們就需要對於這種特殊的需求進行一下相應的調整,這時,我在更新數據庫寫入的時候,我同樣也寫緩存,這樣就可以直接查到緩存數據了。所以,我們在應用這種緩存讀寫策略的時候,也要關注我們自身的業務。

 

Cache Aside策略有什麼缺陷

 

Cache Aside 最大的缺陷就是,當我們寫入操作很頻繁的時候,緩存中的數據就會被頻繁的刪除掉,會直接導致緩存命中率下降,但是如果我們業務中又必須要很高的緩存命中率怎麼辦呢?比如我們公司有個收費接口,每調用一次就得給錢,所以就使用了這種緩存的方案,可以參考下面兩種解決方案:

  1. 在更新數據庫記錄的時候也更新緩存,我們在代碼寫更新緩存前加上分佈式鎖,每次運行一個線程更新緩存,防止併發問題,這種做法就是會對寫入性能帶了一定影響,畢竟加了鎖。

  2. 第二種方案,同樣也是更新數據庫的時候更新緩存,但是這次我們把緩存設置一個過期時間,一般很短,我根據業務需求計算,即使出現了數據不一致的情況,也是會很快就過期了,可以接受,我們現在就是用這種方案來做的。

總結,今天我們學習了使用緩存時,在我們日常開發中應用最多的緩存讀寫策略Cache Aside 旁路緩存策略,然後結合案例給大家演示我們該如何根據自己業務去正確使用緩存的讀寫,解決數據不一致的問題。其實只要有緩存的存在就一定會有數據不一致的問題,我們要做的就是怎麼去權衡他們二者關係然後給出我們接受的方案。

下一篇預告:聊聊我們緩存中的高可用話題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章