數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)一致性問(wèn)題

業(yè)務(wù)使用Redis做緩存,當(dāng)有數(shù)據(jù)更新時(shí),如何保證緩存及時(shí)更新

讀數(shù)據(jù)流程

請(qǐng)求到來(lái),業(yè)務(wù)代碼會(huì)先查Redis,查不到再去查DB,并將結(jié)果寫(xiě)入Redis

寫(xiě)數(shù)據(jù)方案

1. 先刪除緩存,再更新DB

可行性

先刪除緩存,再更新DB,下次讀請(qǐng)求到來(lái)會(huì)從數(shù)據(jù)庫(kù)查到新的數(shù)據(jù)更新到緩存中。如果先更新緩存,在更新DB,更新DB失敗會(huì)導(dǎo)致數(shù)據(jù)不一致。

問(wèn)題

容災(zāi)不足

如果刪除緩存失敗的情況,如果業(yè)務(wù)繼續(xù)進(jìn)行,更新DB,那么在緩存過(guò)期之前仍然查到的是舊數(shù)據(jù)。如果業(yè)務(wù)返回失敗,則對(duì)Redis變成了強(qiáng)依賴(lài)。

并發(fā)不安全

考慮如下場(chǎng)景:

  1. A請(qǐng)求刪除緩存,A請(qǐng)求更新DB
  2. B請(qǐng)求查詢(xún)緩存,不存在
  3. B請(qǐng)求查詢(xún)DB,查到舊數(shù)據(jù)(更新未完成),寫(xiě)入緩存
  4. A請(qǐng)求更新DB完成

這就導(dǎo)致緩存中仍存的舊數(shù)據(jù),數(shù)據(jù)不一致。

2. 先更新DB,再刪除緩存

這種策略解決了方法1中的并發(fā)問(wèn)題,但是還是有極小可能存在并發(fā)問(wèn)題,考慮如下情況:

  1. 請(qǐng)求A查詢(xún)緩存,緩存剛好失效
  2. 請(qǐng)求A查詢(xún)DB,得到一個(gè)舊值
  3. 請(qǐng)求B更新數(shù)據(jù)庫(kù)
  4. 請(qǐng)求B刪除緩存
  5. 請(qǐng)求A將查到的舊值寫(xiě)入緩存

這種情況確實(shí)會(huì)產(chǎn)生數(shù)據(jù)不一致,但是考慮到DB的讀操作總是比寫(xiě)操作快的多,這種場(chǎng)景基本不可能出現(xiàn)。

如何杜絕并發(fā)問(wèn)題

延遲異步刪,保證讀操作完成后再刪除緩存。

如何容災(zāi)

上述方案中如果刪除緩存失敗了怎么辦?

引入消息隊(duì)列
  1. 更新DB
  2. 刪除緩存,如果失敗將要?jiǎng)h除的key發(fā)送至消息隊(duì)列
  3. 消費(fèi)消息,獲得需要?jiǎng)h除的key,刪除key緩存直到成功
訂閱binlog

上述方法對(duì)業(yè)務(wù)代碼的侵入性比較大,為此可以啟動(dòng)一個(gè)程序訂閱MySQL的binlog用來(lái)發(fā)現(xiàn)數(shù)據(jù)更新,流程如下:

  1. 業(yè)務(wù)代碼更新數(shù)據(jù)庫(kù),MySQL將更新操作寫(xiě)入binlog
  2. 訂閱程序提取中更新的數(shù)據(jù)以及key,嘗試刪除key的緩存
  3. 如果刪除緩存失敗,將key發(fā)送至消息隊(duì)列
  4. 消費(fèi)者程序從消息隊(duì)列中獲取待刪除的key,重試刪除直到成功。

參考

【1】Redis與Mysql雙寫(xiě)一致性方案解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容