大家好,我是老三,今天又是被演算法致鬱的一天,寫篇文章緩一緩。
這篇文章,我們來看看快取一致性問題。
快取一致性
我接下來會巴巴說一堆快取一致性,但是——
作為一名暴躁老哥,我先把結論撂這了!
快取和資料庫的強一致性無法實現!
CAP理論瞭解一下,快取適用的場景屬於CAP中的AP,是非強一致性的場景。
那還扯個犢子的快取一致性?洗洗睡吧。
BASE理論接著瞭解一下,強一致性保證不了,那隻好委屈求全,儘量保證最終一致性唄。
最終一致性強調的是系統中所有的資料副本,在經過一段時間的同步後,最終能夠達到一個一致的狀態。因此,最終一致性的本質是需要系統保證最終資料能夠達到一致,而不需要實時保證系統資料的強一致性。
所以,我們追求的是儘可能保證快取和資料庫的最終一致性。
先更新資料庫,再刪除快取
Cache Aside Pattern
在開始之前,我們先來科普一下快取+資料庫讀寫,最經典的Cache Aside Pattern。
- 讀取:先讀取快取,快取裡沒有,讀取資料庫,然後返回響應,順斌儲存快取
- 更新:先更新資料庫,然後刪除快取
為什麼是刪除快取,而不是更新快取?
- 併發情況下更新快取可能會帶來種種問題,直接刪除快取更加穩妥。
- 快取更新在很多時候需要耗費資源,直接刪除,用時再從資料庫讀取,寫進快取,更省效能。
一致性問題
那麼我們採用這種先更新資料庫,再刪除快取,可能會出現什麼問題呢?
假如,我們更新資料庫成功,接下來還沒來刪除快取,或者刪除快取失敗怎麼辦?
那麼很明顯,這時候其它執行緒進來讀的就是髒資料。
那怎麼解決呢?
解決方案
既然刪除快取失敗會導致髒資料,那我們就想辦法讓它能刪除成功唄。
訊息佇列重試機制
我們可以引入一個重試機制。
如果刪除快取失敗,向訊息佇列傳送訊息,把刪除失敗的key放進去,消費訊息佇列,獲取要刪除的key,然後去重試刪除。
但是,這麼幹,好好的業務,我們們又引入了訊息佇列,對現有的業務造成了入侵,複雜度又提升了。
監聽binlog非同步刪除
其實還有另外一種辦法,我們可以用一個服務(比如阿里的 canal)去監聽資料庫的binlog,獲取需要操作的資料。
然後用另外一個服務獲取訂閱程式傳來的資訊,進行快取刪除操作。
這樣一來,對我們本身的業務入侵就小了很多。
先刪除快取,再更新資料庫
一致性問題
我們看一下,如果先刪除快取,再更新資料庫可能會帶來什麼問題。
在併發情況下,先刪除快取,再更新資料庫,此時資料庫還未更新成功,這時候有其它執行緒進來了,讀取快取,快取不存在,讀取資料庫,讀取的是舊值,這時候,快取不一致就發生了。
解決方案
延時雙刪
延時雙刪是什麼意思呢?
就是在刪除快取,更新資料庫之後,休眠一段時間後,再次刪除快取。
延時刪除之後,就把快取裡快取的舊值給刪除了。
再有請求進來,就是讀取資料庫裡的新值,再把新值儲存進快取。
當然,第二次刪除也有失敗的可能,怎麼辦呢?重試。那怎麼重試呢?前面寫了。
關於刪除,還有一個兜底的方案——設定快取過期時間
,這樣一來,哪怕快取了髒資料,但是髒資料總有過期的時候,不至於一直不一致。
總結
我們來簡單總結一下,首先對快取的操作,刪除優於更新,所以要刪除,而不是更新。
刪除快取兩種方式:
- 先更新資料庫,在刪除快取。快取不一致的兩種處理方式是
訊息佇列重試機制
和binlog非同步刪除
。 - 先刪除快取,再更新資料庫。快取不一致的處理方式是
延時雙刪
。
當然,這些方案無疑都增加了系統的複雜度。
如果不是併發特別高的話,就沒有必要過度設計。
簡單的事情重複做,重複的事情認真做,認真的事情有創造性地做。
我是三分惡,一個努力學習中的程式設計師。
點贊
、關注
不迷路,我們們下期見!
參考:
[1]. 快取與資料庫一致性問題深度剖析
[3]. 面試官:快取一致性問題怎麼解決?