在分佈式系統中,緩存和數據庫同時存在時,若是有寫操做,先操做數據庫仍是先操做緩存呢?本文將分5種方案闡述展開對比,謝謝閱讀~mysql
github地址,衷心感謝每一顆stargit
❝https://github.com/whx123/JavaHomegithub
❞
若是是一讀(線程B)一寫(線程A)操做,「先操做緩存,再操做數據庫」。流程圖以下所示:web
1.線程A發起一個寫操做,第一步del cachesql
2.線程A第二步寫入新數據到DB數據庫
3.線程B發起一個讀操做,cache miss緩存失效了。緩存
4.線程B從DB獲取最新數據併發
5.線程B執行set cache,把從DB讀到的數據,更新到緩存。異步
「這樣看,沒啥問題」。咱們再看第二個流程圖,以下:編輯器
1.線程A發起一個寫操做,第一步del cache
2.此時線程B發起一個讀操做,cache miss
3.線程B繼續讀DB,讀出來一個老數據
4.而後老數據設置入cache
5.線程A寫入DB最新的數據
OK,醬紫,就有問題了吧,老數據入到緩存了,「每次讀都是老數據啦,緩存與數據與數據庫數據不一致了」。
上個方案是一讀一寫,若是是雙寫操做,「先操做緩存,在操做數據庫」,會怎麼樣呢?
1.線程A發起一個寫操做,第一步set cache
2.線程A第二步寫入新數據到DB
3.線程B發起一個寫操做,set cache
4.線程B第二步寫入新數據到DB
「這樣看,也沒啥問題。」,可是有時候可能事與願違,咱們再看第二個流程圖,以下:
1.線程A發起一個寫操做,第一步set cache
2.線程B發起一個寫操做,第一步set cache
3.線程B寫入數據庫到DB
4.線程A寫入數據庫到DB
執行完後,緩存保存的是B操做後的數據,數據庫是A操做後的數據,「緩存和數據庫數據不一致」。
一寫(線程A)一讀(線程B)操做,「先操做數據庫,再操做緩存」。
1.線程A發起一個寫操做,第一步write DB
2.線程A第二步del cache
3.線程B發起一個讀操做,cache miss
4.線程B從DB獲取最新數據
5.線程B同時set cache
這種方案「沒有明顯的併發問題」,可是有可能「步驟二刪除緩存失敗」,雖然機率比較小,「優於方案一和方案二」,平時工做中也是使用方案三。
綜上對比,咱們通常採用方案三,可是有沒有完美全解決方案三的弊端的方法呢?
這個是方案三的改進方案,都是先操做數據庫再操做緩存,咱們來看一下流程圖:
經過數據庫的「binlog」來「異步淘汰key」,以mysql爲例 能夠「使用阿里的canal將binlog日誌採集發送到MQ隊列」裏面,而後「經過ACK機制 確認處理」 這條更新消息,刪除緩存,保證數據緩存一致性。
可是呢還有個問題,「若是是主從數據庫呢」?
主從DB問題:由於主從DB同步存在延時時間。若是刪除緩存以後,數據同步到備庫以前已經有請求過來時,「會從備庫中讀到髒數據」,如何解決呢?解決方案以下流程圖:
綜上所述,在分佈式系統中,緩存和數據庫同時存在時,若是有寫操做的時候,「先操做數據庫,再操做緩存」。以下: