具體流程

讀流程

緩存+db 該怎麼設計?_更新數據

寫流程

緩存+db 該怎麼設計?_更新數據_02


寫流程要記住兩點

  • 1 先更新數據庫再更新緩存
  • 2 更新緩存的時候就直接刪除,不要update成新值。
  • 3 如果刪除緩存失敗了,整體對外的寫請求仍然返回成功,內部再重試去刪除。

為啥要先更新數據庫,如果先刪除緩存不行嗎?

緩存+db 該怎麼設計?_更新數據_03


線程A發起一個寫操作,第一步del cache

此時線程B發起一個讀操作,cache miss

線程B繼續讀DB,讀出來一個老數據

然後線程B把老數據設置入cache

線程A寫入DB最新的數據

醬紫就有問題啦,緩存和數據庫的數據不一致了。緩存保存的是老數據,數據庫保存的是新數據。

先改db再刪緩存的不一致現象

緩存+db 該怎麼設計?_#db_04


以上面的時間線來説,最終db的數據是新的,緩存的數據是老的

依然是不一致的

可行的方案1

寫: 寫db->刪緩存->寫緩存

讀:讀緩存發現沒有->讀db->寫緩存

緩存+db 該怎麼設計?_數據_05


如上,左邊的圖

讀進程第六步的寫緩存在前面讀進程第五步的寫緩存之後,寫緩存的時候,使用set not exist,如果失敗,那麼整個讀請求都失敗

為什麼讓讀失敗呢?因為讀請求寫進去的數據 已經是髒數據了。

如果是右邊的圖

寫進程的第六步寫緩存在前面讀進程第五步寫緩存之後,寫緩存的時候,使用set not exist,如果失敗,那寫請求就失敗.(數據回滾)

那這裏為什麼寫進程要失敗呢?此時雖然讀寫併發了,但是數據依然是一致的呀!為什麼呢?

這樣設計的原因是:寫寫也會有併發。其實最終的方案還是比較麻煩的,建議就使用上文説的寫請求時 先寫db,然後刪除緩存就好。

可是這個方案會有不一致的情況呀。

是有不一致情況,但是這個概率比較小。

緩存+db 該怎麼設計?_更新數據_06


上圖不管是左邊的還是右邊的,都要求寫請求的第三步,必須在讀請求的讀db與寫緩存之間。但是實際上,寫db比寫緩存慢多了,所以絕大部分情況都是寫請求更新緩存的時候,讀請求那邊寫緩存的操作都已經完成了.

可行的方案2

延遲雙刪,那就是每次寫進程先寫db之後,刪除了緩存之後,再發一個消息,然後等1秒或者2秒再把原來的緩存刪除一遍。當然這就有一個問題,如果數據經常更新,那豈不是要經常發消息?
是的,是需要經常發消息
那我問一個問題,既然這個數據經常更新,那還有必要放到緩存裏面麼?

一點思考

有沒有發現考慮到極端的一致性問題,解決思路就會比較麻煩,都得引入新的流程,怎麼保證方案既簡單,又能應對所有的方案呢?
沒有,如果有這種方案,我直接在文檔開始就會告訴大家,哪裏會撤這麼多。
問題複雜和解決方案複雜是一個兩難問題,我認為是沒有一個完全通用的方案的,就看你的取捨。(舉個例子,現實場景我就把數據放到mysql,不用緩存了,不就沒有不一致的問題。有人説你這只是規避問題而不是解決問題,我想説,試卷上每個問題都得回答,哪個沒有回答就沒有分,但是在現實生產環境下,我們不是做題,有時候解決一個問題的最好方法就是恰好不去解決它。畢竟我們不是搞學術研究)