上回程序員阿旺為了提升數(shù)據(jù)訪問的性能,引入 Redis 作為 MySQL 緩存層,但是這件事情并不是那么簡單,因為還要考慮 Redis 和 MySQL 雙寫一致性的問題。阿旺經(jīng)過一番周折,最終選用了「先更新數(shù)據(jù)庫,再刪緩存」的策略,原因是這個策略即使在并發(fā)讀寫時,也能最大程度保證數(shù)據(jù)一致性。聰明的阿旺還搞了個兜底的方案,就是給緩存加上了過期時間。本以為就這樣不會在出現(xiàn)數(shù)據(jù)一致性的問題,結(jié)果將功能上線后,老板還是收到用戶的投訴「說自己明明更新了數(shù)據(jù),但是數(shù)據(jù)要過一段時間才生效」,客戶接受不了。老板轉(zhuǎn)告給了阿旺,阿旺得知又有 Bug 就更慌了,立馬就登錄服務(wù)器去排查問題,查看日志后得知了原因?!赶雀聰?shù)據(jù)庫, 再刪除緩存」其實是兩個操作,這次客戶投訴的問題就在于,在刪除緩存(第二個操作)的時候失敗了,導致緩存中的數(shù)據(jù)是舊值,而數(shù)據(jù)庫是最新值。好在之前給緩存加上了過期時間,所以才會出現(xiàn)客戶說的過一段時間才更新生效的現(xiàn)象,假設(shè)如果沒有這個過期時間的兜底,那后續(xù)的請求讀到的就會一直是緩存中的舊數(shù)據(jù),這樣問題就更大了。所以新的問題來了,如何保證「先更新數(shù)據(jù)庫 ,再刪除緩存」這兩個操作能執(zhí)行成功?阿旺分析出問題后,慌慌張張的向老板匯報了問題。老板知道事情后,又給了阿旺幾天來解決這個問題,畫餅的事情這次沒有再提了。
阿旺會用什么方式來解決這個問題呢?
老板畫的餅事情,能否兌現(xiàn)給阿旺呢?
如何保證兩個操作都能執(zhí)行成功?
這次用戶的投訴是因為在刪除緩存(第二個操作)的時候失敗了,導致緩存還是舊值,而數(shù)據(jù)庫是最新值,造成數(shù)據(jù)庫和緩存數(shù)據(jù)不一致的問題,會對敏感業(yè)務(wù)造成影響。舉個例子,來說明下。應(yīng)用要把數(shù)據(jù) X 的值從 1 更新為 2,先成功更新了數(shù)據(jù)庫,然后在 Redis 緩存中刪除 X 的緩存,但是這個操作卻失敗了,這個時候數(shù)據(jù)庫中 X 的新值為 2,Redis 中的 X 的緩存值為 1,出現(xiàn)了數(shù)據(jù)庫和緩存數(shù)據(jù)不一致的問題。那么,后續(xù)有訪問數(shù)據(jù) X 的請求,會先在 Redis 中查詢,因為緩存并沒有 誒刪除,所以會緩存命中,但是讀到的卻是舊值 1。其實不管是先操作數(shù)據(jù)庫,還是先操作緩存,只要第二個操作失敗都會出現(xiàn)數(shù)據(jù)一致的問題。問題原因知道了,該怎么解決呢?有兩種方法: