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