大家好,我是老三,今天又是被演算法致郁的一天,寫篇文章緩一緩,
這篇文章,我們來看看快取一致性問題,
快取一致性
我接下來會巴巴說一堆快取一致性,但是——
作為一名暴躁老哥,我先把結論撂這了!
快取和資料庫的強一致性無法實作!
CAP理論了解一下,快取適用的場景屬于CAP中的AP,是非強一致性的場景,
那還扯個犢子的快取一致性?洗洗睡吧,
BASE理論接著了解一下,強一致性保證不了,那只好委屈求全,盡量保證最終一致性唄,
最終一致性強調的是系統中所有的資料副本,在經過一段時間的同步后,最終能夠達到一個一致的狀態,因此,最終一致性的本質是需要系統保證最終資料能夠達到一致,而不需要實時保證系統資料的強一致性,
所以,我們追求的是盡可能保證快取和資料庫的最終一致性,

先更新資料庫,再洗掉快取
Cache Aside Pattern
在開始之前,我們先來科普一下快取+資料庫讀寫,最經典的Cache Aside Pattern,
- 讀取:先讀取快取,快取里沒有,讀取資料庫,然后回傳回應,順斌保存快取

- 更新:先更新資料庫,然后洗掉快取

為什么是洗掉快取,而不是更新快取?
- 并發情況下更新快取可能會帶來種種問題,直接洗掉快取更加穩妥,
- 快取更新在很多時候需要耗費資源,直接洗掉,用時再從資料庫讀取,寫進快取,更省性能,
一致性問題
那么我們采用這種先更新資料庫,再洗掉快取,可能會出現什么問題呢?
假如,我們更新資料庫成功,接下來還沒來洗掉快取,或者洗掉快取失敗怎么辦?
那么很明顯,這時候其它執行緒進來讀的就是臟資料,

那怎么解決呢?
解決方案
既然洗掉快取失敗會導致臟資料,那我們就想辦法讓它能洗掉成功唄,
訊息佇列重試機制
我們可以引入一個重試機制,
如果洗掉快取失敗,向訊息佇列發送訊息,把洗掉失敗的key放進去,消費訊息佇列,獲取要洗掉的key,然后去重試洗掉,

但是,這么干,好好的業務,咱們又引入了訊息佇列,對現有的業務造成了入侵,復雜度又提升了,
監聽binlog異步洗掉
其實還有另外一種辦法,我們可以用一個服務(比如阿里的 canal)去監聽資料庫的binlog,獲取需要操作的資料,
然后用另外一個服務獲取訂閱程式傳來的資訊,進行快取洗掉操作,

這樣一來,對我們本身的業務入侵就小了很多,
先洗掉快取,再更新資料庫
一致性問題
我們看一下,如果先洗掉快取,再更新資料庫可能會帶來什么問題,
在并發情況下,先洗掉快取,再更新資料庫,此時資料庫還未更新成功,這時候有其它執行緒進來了,讀取快取,快取不存在,讀取資料庫,讀取的是舊值,這時候,快取不一致就發生了,

解決方案
延時雙刪
延時雙刪是什么意思呢?
就是在洗掉快取,更新資料庫之后,休眠一段時間后,再次洗掉快取,

延時洗掉之后,就把快取里快取的舊值給洗掉了,
再有請求進來,就是讀取資料庫里的新值,再把新值保存進快取,
當然,第二次洗掉也有失敗的可能,怎么辦呢?重試,那怎么重試呢?前面寫了,
關于洗掉,還有一個兜底的方案——設定快取過期時間,這樣一來,哪怕快取了臟資料,但是臟資料總有過期的時候,不至于一直不一致,
總結
我們來簡單總結一下,首先對快取的操作,洗掉優于更新,所以要洗掉,而不是更新,
洗掉快取兩種方式:
- 先更新資料庫,在洗掉快取,快取不一致的兩種處理方式是
訊息佇列重試機制和binlog異步洗掉, - 先洗掉快取,再更新資料庫,快取不一致的處理方式是
延時雙刪,
當然,這些方案無疑都增加了系統的復雜度,
如果不是并發特別高的話,就沒有必要過度設計,
簡單的事情重復做,重復的事情認真做,認真的事情有創造性地做,
我是三分惡,一個努力學習中的程式員,
點贊、關注不迷路,咱們下期見!
參考:
[1]. 快取與資料庫一致性問題深度剖析
[2]. 資料庫和快取一致性的幾種實作方式,我們來聊聊?
[3]. 面試官:快取一致性問題怎么解決?
[4]. 美團二面:Redis與MySQL雙寫一致性如何保證?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300702.html
標籤:其他
