redis 14 快取和資料庫雙寫一致性問題詳解

2022-07-08 01:18:07 字數 3587 閱讀 5608

如果我們的資料在快取裡邊有,那麼就直接取快取的。

如果快取裡沒有我們想要的資料,我們會先去查詢資料庫,然後 將資料庫查出來的資料寫到快取中。

最後將資料返回給請求。

如果僅僅查詢的話,快取的資料和資料庫的資料是沒問題的。但是,當我們要更新時候呢?各種情況很可能就造成資料庫 和 快取的資料不一致了。

從理論上說,只要我們設定了鍵的過期時間,我們就能保證快取 和 資料庫的資料最終是一致的。因為只要快取資料過期了,就會被刪除。隨後讀的時候,因為快取裡沒有,就可以查資料庫的資料,然後將資料庫查出來的資料寫入到快取中。

除了設定過期時間,我們還需要做更多的措施來盡量避免資料庫 與 快取處於不一致的情況發生。

一般來說,執行更新操作時,我們會有兩種選擇:

先運算元據庫,再操作快取

先操作快取,再運算元據庫

首先,要明確的是,無論我們選擇哪個,我們都希望這兩個操作要麼同時成功,要麼同時失敗。所以,這會演變成乙個分布式事務的問題。

所有,如果原子性被破壞,可能會有以下的情況:

1.1、操作快取

操作快取也有兩種方案:

1、更新快取

2、刪除快取

一般我們都是採取刪除快取策略的,原因如下:

1、高併發環境下,無論是先運算元據庫還是後運算元據庫而言,如果加上更新快取,那就更加容易導致資料庫與快取資料不一致問題。(刪除快取直接且簡單很多)

2、如果每次更新了資料庫,都要更新快取【這裡指的是頻繁更新的場景,這會耗費一定的效能】,倒不如直接刪除掉。等再次讀取時,快取裡沒有,那我到資料庫找,在資料庫找到再寫到快取裡邊(體現懶載入)

基於這兩點,對於快取在更新時而言,都是建議執行刪除操作!

1.2、先更新資料庫,再刪除快取

正常的情況是這樣的:

1. 先運算元據庫,成功;

2. 再刪除快取,也成功;

如果原子性被破壞了:

1. 第一步成功(運算元據庫),第二步失敗(刪除快取),會導致 資料庫裡是新資料,而快取裡是舊資料。

2. 如果第一步(運算元據庫)就失敗了,我們可以直接返回錯誤(exception),不會出現資料不一致。

如果在高併發的場景下,出現資料庫與快取資料不一致的概率特別低,也不是沒有:

1. 快取剛好失效

2. 執行緒a查詢資料庫,得乙個舊值

3. 執行緒b將新值寫入資料庫

4. 執行緒b刪除快取

5. 執行緒a將查到的舊值寫入快取

要達成上述情況,還是說一句概率特別低

因為這個條件需要發生在讀快取時快取失效,而且併發著有乙個寫操作。而實際上資料庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進入資料庫操作,而又要晚於寫操作更新快取,所有的這些條件都具備的概率基本並不大。

刪除快取失敗的解決思路

1. 將需要刪除的key傳送到訊息佇列中

2. 自己消費訊息,獲得需要刪除的key

3.不斷重試刪除操作,直到成功

1.3、先刪除快取,再更新資料庫

正常情況是這樣的:

1. 先刪除快取,成功;

2. 再更新資料庫,也成功;

如果原子性被破壞了:

1. 第一步成功(刪除快取),第二步失敗(更新資料庫),資料庫和快取的資料還是一致的。

2. 如果第一步(刪除快取)就失敗了,我們可以直接返回錯誤(exception),資料庫和快取的資料還是一致的。

看起來是很美好,但是我們在併發場景下分析一下,就知道還是有問題的了:

1. 執行緒a刪除了快取

2. 執行緒b查詢,發現快取已不存在

3. 執行緒b去資料庫查詢得到舊值

4. 執行緒b將舊值寫入快取

5. 執行緒a將新值寫入資料庫

所以也會導致資料庫和快取不一致的問題。

併發下解決資料庫與快取不一致的思路

將刪除快取、修改資料庫、讀取快取等的操作積壓到佇列裡邊,實現序列化

方案一:

流程如下所示:

(1)更新資料庫資料;

(2)快取因為種種問題刪除失敗

(3)將需要刪除的key傳送至訊息佇列

(4)自己消費訊息,獲得需要刪除的key

(5)繼續重試刪除操作,直到成功

然而,該方案有乙個缺點,對業務線**造成大量的侵入。於是有了方案二,在方案二中,啟動乙個訂閱程式去訂閱資料庫的binlog,獲得需要操作的資料。在應用程式中,另起一段程式,獲得這個訂閱程式傳來的資訊,進行刪除快取操作。

方案二:

流程如下圖所示:

(1)更新資料庫資料

(2)資料庫會將操作資訊寫入binlog日誌當中

(3)訂閱程式提取出所需要的資料以及key

(4)另起一段非業務**,獲得該資訊

(5)嘗試刪除快取操作,發現刪除失敗

(6)將這些資訊傳送至訊息佇列

(7)重新從訊息佇列中獲得該資料,重試操作。

備註說明:

上述的訂閱binlog程式在mysql中有現成的中介軟體叫canal,可以完成訂閱binlog日誌的功能。另外,重試機制,採用的是訊息佇列的方式。如果對一致性要求不是很高,直接在程式中另起乙個執行緒,每隔一段時間去重試即可,這些大家可以靈活自由發揮,只是提供乙個思路。

本文其實是對目前網際網路中已有的一致性方案,進行了乙個總結。

對於先刪快取,再更新資料庫的更新策略,還有方案提出維護乙個記憶體佇列的方式,看了一下,覺得實現異常複雜,沒有必要,因此沒有必要在文中給出。

面試官:你在實際專案中使用快取有遇到什麼問題或者會遇到什麼問題你知道嗎?

我:快取和資料庫資料一致性問題:分布式環境下非常容易出現快取和資料庫間資料一致性問題,針對這一點,如果專案對快取的要求是強一致性的,那麼就不要使用快取。我們只能採取合適的策略來降低快取和資料庫間資料不一致的概率,而無法保證兩者間的強一致性。合適的策略包括合適的快取更新策略,更新資料庫後及時更新快取、快取失敗時增加重試機制。具體方案策略可以參考上面第二種方案。

如何保證Redis快取和資料庫的雙寫一致性?

在資料庫 快取模式下,當資料庫中的資料需要更新時,快取裡的資料怎麼處理?如何保證快取和資料庫中資料的一致性?常用的解決方案有兩種 其他渣渣的方案這裡不討論 1 先刪除快取,再更新資料庫 2 先更新資料庫,再刪除快取 下面我們就來看一下這兩種方案,看看它們是怎麼保證資料一致性的?理想的流程是這樣的 先...

Redis快取和資料庫雙寫一致性問題

資料庫與快取讀寫模式策略 寫完資料庫後是否需要馬上更新快取還是直接刪除快取?1 如果寫資料庫的值與更新到快取值是一樣的,不需要經過任何的計算,可以馬上更新快取,但是如果對於那種寫資料頻繁而讀資料少的場景並不合適這種解決方案,因為也許還沒有查詢就被刪除或修改了,這樣會浪費時間和資源 2 如果寫資料庫的...

redis 12 快取和資料庫雙寫一致性問題

資料庫中的資料和快取中的資料不一致 如果我們只是讀操作,肯定不會存在快取和資料庫雙寫一致性問題。但是如果更新或者刪除操作呢?我們知道執行乙個更新操作花費的時間遠遠大於乙個讀操作花費的時間。更新操作,我們是先更新資料庫呢還是先更新快取呢?如果兩步操作符合原子性,要麼同時成功,要麼同時失敗,則不存在一致...