天天看點

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

大家好,我是老三,今天又是被算法緻郁的一天,寫篇文章緩一緩。

這篇文章,我們來看看緩存一緻性問題。

緩存一緻性

我接下來會巴巴說一堆緩存一緻性,但是——

作為一名暴躁老哥,我先把結論撂這了!

緩存和資料庫的強一緻性無法實作!

CAP理論了解一下,緩存适用的場景屬于CAP中的AP,是非強一緻性的場景。

那還扯個犢子的緩存一緻性?洗洗睡吧。

BASE理論接着了解一下,強一緻性保證不了,那隻好委屈求全,盡量保證最終一緻性呗。

最終一緻性強調的是系統中所有的資料副本,在經過一段時間的同步後,最終能夠達到一個一緻的狀态。是以,最終一緻性的本質是需要系統保證最終資料能夠達到一緻,而不需要實時保證系統資料的強一緻性。

是以,我們追求的是盡可能保證緩存和資料庫的最終一緻性。

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

先更新資料庫,再删除緩存

Cache Aside Pattern

在開始之前,我們先來科普一下緩存+資料庫讀寫,最經典的Cache Aside Pattern。

  • 讀取:先讀取緩存,緩存裡沒有,讀取資料庫,然後傳回響應,順斌儲存緩存
緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結
  • 更新:先更新資料庫,然後删除緩存
緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

為什麼是删除緩存,而不是更新緩存?

  • 并發情況下更新緩存可能會帶來種種問題,直接删除緩存更加穩妥。
  • 緩存更新在很多時候需要耗費資源,直接删除,用時再從資料庫讀取,寫進緩存,更省性能。

一緻性問題

那麼我們采用這種先更新資料庫,再删除緩存,可能會出現什麼問題呢?

假如,我們更新資料庫成功,接下來還沒來删除緩存,或者删除緩存失敗怎麼辦?

那麼很明顯,這時候其它線程進來讀的就是髒資料。

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

那怎麼解決呢?

解決方案

既然删除緩存失敗會導緻髒資料,那我們就想辦法讓它能删除成功呗。

消息隊列重試機制

我們可以引入一個重試機制。

如果删除緩存失敗,向消息隊列發送消息,把删除失敗的key放進去,消費消息隊列,擷取要删除的key,然後去重試删除。

但是,這麼幹,好好的業務,咱們又引入了消息隊列,對現有的業務造成了入侵,複雜度又提升了。

監聽binlog異步删除

其實還有另外一種辦法,我們可以用一個服務(比如阿裡的 canal)去監聽資料庫的binlog,擷取需要操作的資料。

然後用另外一個服務擷取訂閱程式傳來的資訊,進行緩存删除操作。

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

這樣一來,對我們本身的業務入侵就小了很多。

先删除緩存,再更新資料庫

我們看一下,如果先删除緩存,再更新資料庫可能會帶來什麼問題。

在并發情況下,先删除緩存,再更新資料庫,此時資料庫還未更新成功,這時候有其它線程進來了,讀取緩存,緩存不存在,讀取資料庫,讀取的是舊值,這時候,緩存不一緻就發生了。

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

延時雙删

延時雙删是什麼意思呢?

就是在删除緩存,更新資料庫之後,休眠一段時間後,再次删除緩存。

緩存一緻性?get緩存一緻性先更新資料庫,再删除緩存先删除緩存,再更新資料庫總結

延時删除之後,就把緩存裡緩存的舊值給删除了。

再有請求進來,就是讀取資料庫裡的新值,再把新值儲存進緩存。

當然,第二次删除也有失敗的可能,怎麼辦呢?重試。那怎麼重試呢?前面寫了。

關于删除,還有一個兜底的方案——

設定緩存過期時間

,這樣一來,哪怕緩存了髒資料,但是髒資料總有過期的時候,不至于一直不一緻。

總結

我們來簡單總結一下,首先對緩存的操作,删除優于更新,是以要删除,而不是更新。

删除緩存兩種方式:

  • 先更新資料庫,在删除緩存。緩存不一緻的兩種處理方式是

    消息隊列重試機制

    binlog異步删除

  • 先删除緩存,再更新資料庫。緩存不一緻的處理方式是

    延時雙删

當然,這些方案無疑都增加了系統的複雜度。

如果不是并發特别高的話,就沒有必要過度設計。

簡單的事情重複做,重複的事情認真做,認真的事情有創造性地做。

我是三分惡,一個努力學習中的程式員。

點贊

關注

不迷路,咱們下期見!