具體流程
讀流程
寫流程
寫流程要記住兩點
- 1 先更新數據庫再更新緩存
- 2 更新緩存的時候就直接刪除,不要update成新值。
- 3 如果刪除緩存失敗了,整體對外的寫請求仍然返回成功,內部再重試去刪除。
為啥要先更新數據庫,如果先刪除緩存不行嗎?
線程A發起一個寫操作,第一步del cache
此時線程B發起一個讀操作,cache miss
線程B繼續讀DB,讀出來一個老數據
然後線程B把老數據設置入cache
線程A寫入DB最新的數據
醬紫就有問題啦,緩存和數據庫的數據不一致了。緩存保存的是老數據,數據庫保存的是新數據。
先改db再刪緩存的不一致現象
以上面的時間線來説,最終db的數據是新的,緩存的數據是老的
依然是不一致的
可行的方案1
寫: 寫db->刪緩存->寫緩存
讀:讀緩存發現沒有->讀db->寫緩存
如上,左邊的圖
讀進程第六步的寫緩存在前面讀進程第五步的寫緩存之後,寫緩存的時候,使用set not exist,如果失敗,那麼整個讀請求都失敗
為什麼讓讀失敗呢?因為讀請求寫進去的數據 已經是髒數據了。
如果是右邊的圖
寫進程的第六步寫緩存在前面讀進程第五步寫緩存之後,寫緩存的時候,使用set not exist,如果失敗,那寫請求就失敗.(數據回滾)
那這裏為什麼寫進程要失敗呢?此時雖然讀寫併發了,但是數據依然是一致的呀!為什麼呢?
這樣設計的原因是:寫寫也會有併發。其實最終的方案還是比較麻煩的,建議就使用上文説的寫請求時 先寫db,然後刪除緩存就好。
可是這個方案會有不一致的情況呀。
是有不一致情況,但是這個概率比較小。
上圖不管是左邊的還是右邊的,都要求寫請求的第三步,必須在讀請求的讀db與寫緩存之間。但是實際上,寫db比寫緩存慢多了,所以絕大部分情況都是寫請求更新緩存的時候,讀請求那邊寫緩存的操作都已經完成了.
可行的方案2
延遲雙刪,那就是每次寫進程先寫db之後,刪除了緩存之後,再發一個消息,然後等1秒或者2秒再把原來的緩存刪除一遍。當然這就有一個問題,如果數據經常更新,那豈不是要經常發消息?
是的,是需要經常發消息
那我問一個問題,既然這個數據經常更新,那還有必要放到緩存裏面麼?
一點思考
有沒有發現考慮到極端的一致性問題,解決思路就會比較麻煩,都得引入新的流程,怎麼保證方案既簡單,又能應對所有的方案呢?
沒有,如果有這種方案,我直接在文檔開始就會告訴大家,哪裏會撤這麼多。
問題複雜和解決方案複雜是一個兩難問題,我認為是沒有一個完全通用的方案的,就看你的取捨。(舉個例子,現實場景我就把數據放到mysql,不用緩存了,不就沒有不一致的問題。有人説你這只是規避問題而不是解決問題,我想説,試卷上每個問題都得回答,哪個沒有回答就沒有分,但是在現實生產環境下,我們不是做題,有時候解決一個問題的最好方法就是恰好不去解決它。畢竟我們不是搞學術研究)