天天看點

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

作者:馬士兵教育CTO
總算搞懂了!困擾我的Redis緩存與DB一緻性問題

使用緩存的時候,我們需要關注Redis與DB資料的一緻性。如果Redis緩存與DB資料不一緻,就可能導緻使用者一直隻能擷取到資料錯誤的緩存,嚴重影響使用者體驗。那如何讓Redis與DB資料一緻性呢?

如何保證資料庫和緩存的一緻性

首先我們來讨論幾個更新時的方案吧,我将從各個方案進行剖析,讓你知道這些方案會出現的問題,并且最終會得出如何保證資料庫和緩存的一緻性。(使用的圖解皆來源于網絡)

先更新緩存再更新資料庫

我們來舉個例子,在并發情況下,請求A 和 請求B 兩個請求,同時更新同一個資料時,什麼樣的執行流程會導緻資料不一緻。

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

如圖,如果請求A先更新了緩存(值為1),請求B在它之後也更新了緩存(值為2),然後請求B繼續執行,把資料庫更新為2,之後請求A也繼續執行,把資料庫更新為1。

此時,緩存的值為2,資料庫的值為1。很明顯,出現了緩存不一緻的情況。那我們再換一種思路試試看?

先更新資料庫再更新緩存

我們也來分析一下這種情況下,什麼樣的順序會出現緩存不一緻。

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

請求A把資料庫資料更新為1,之後請求B把資料庫更新為2。接着,緩存被請求B更新為2,緩存再被請求A更新為1。

很明顯,這種思路也會出現資料不一緻的情況。至此,同時更新緩存和資料庫的方案,都明顯出現了資料不一緻的現象,那還有什麼其他的思路可以用嗎?

有的,更新資料庫的同時删除緩存,當緩存未命中時,查詢緩存。那我們按照這個思路來進行分析。

先删除緩存再更新資料庫

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

如圖,我們能夠分析出,請求A先删除了緩存,請求B在其之後發現緩存未命中然後就去讀取資料庫更新緩存,最後請求A再更新了資料庫。此時緩存為20,資料庫為21,出現了不一緻。實際上,隻有資料庫更新後,再被查詢資料時,才會是更新後的資料。按照這個思路,又引出了另一種政策。

先更新資料庫再删除緩存

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

請求A發現緩存沒有命中,然後去請求資料庫,之後請求B更新了資料庫并删除了緩存,請求A再将資料寫入緩存。可以發現,依舊是有不一緻的情況發生。

前面四種政策應該怎麼選擇

從上面的分析來看,四種政策都是會有不一緻的現象發生的。雖然是這樣的沒錯,但是先更新資料庫再删除緩存的不一緻現象發生的機率會小一些,因為資料庫更新是沒有緩存寫入快的,是以在實際中很難出現請求 B 已經更新了資料庫并且删除了緩存,請求 A 才更新完緩存的情況。

是以先更新資料庫再删除緩存是可以在一定程度上保證一緻性的,對于可能發生的不一緻情況,我們可以給資料加上過期時間作為對一緻性的兜底。

不過這也僅僅是在我們的操作全部執行成功的情況下才能保證一緻性,在實際情況中,由于網絡等因素,我們的操作是可能會失敗的,而更新資料庫和删除緩存是兩個操作(非原子),是以當删除緩存失敗的時候,一緻性就無法保證了。

但是在一般的業務上,這四種政策已經是足夠使用了,一般使用第四種。對于不同場景下,還是有不同的選擇,并不一定無腦選擇第四種。當你的業務是對于緩存命中率要求高的業務時,可以采用先更新資料庫,再更新緩存,不過我們必須引入一些其他方法來減少不一緻發生的情況。例如:

  • 在更新完緩存時,給緩存加上較短的過期時間,這樣即使出現緩存不一緻的情況,緩存的資料也很快會保持一緻。

在其他情況下,就無腦使用先更新資料庫再更新緩存就行。

有沒有真正能夠保證一緻性的方案

先說結論,是有的,但是會更加複雜。

首先我們要明确:其實不管是先操作資料庫,還是先操作緩存,隻要删除緩存操作失敗都會出現資料一緻的問題。

問題原因知道了,該怎麼解決呢?有兩種方法:

1.重試機制

我們可以引入消息隊列,将第二個操作(删除緩存)要操作的資料加入到消息隊列,由消費者來操作資料。

重試機制的大緻步驟:

  • 寫請求更新資料庫
  • 緩存因為某些原因,删除失敗
  • 把删除失敗的key放到消息隊列
  • 消費消息隊列的消息,擷取要删除的key
  • 重試删除緩存操作

弊端是造成大量業務代碼入侵。不過這個是小事情。

2.訂閱Mysql binlog

先更新資料庫,再删緩存的政策的第一步是更新資料庫,那麼更新資料庫成功,就會産生一條變更日志,記錄在 binlog 裡。

于是我們就可以通過訂閱 binlog 日志,拿到具體要操作的資料,然後再執行緩存删除,阿裡巴巴開源的 Canal 中間件就是基于這個實作的。

總算搞懂了!困擾我的Redis緩存與DB一緻性問題

這個方法我是在網上偶然看見的,還沒有嘗試過,有興趣的小夥伴可以深入去了解下。

希望我的文章對大家學習緩存與DB一緻性有所幫助。

繼續閱讀