先更新緩存仍是先更新數據庫

概覽

說這個問題以前得看下幾種緩存模式,能夠先看下緩存模式(Cache Aside、Read Through、Write Through、Write Behind)這篇文章。html

先更新緩存,再更新數據庫

考慮併發操做:線程A寫,線程B讀

先更新緩存再更新數據庫1

  • 一、線程A發起一個寫操做,第一步delete cache
  • 二、此時線程B發起一個讀操做,cache miss
  • 三、線程B繼續讀數據庫,讀出來一個老數據
  • 四、而後老數據入cache
  • 五、線程A寫入了最新的數據

這樣之後每次從緩存中讀到的都是老數據,數據不一致,不能知足咱們的需求。shell

既然這種狀況下先刪除緩存會有數據不一致的狀況,那咱們來試試第一步不刪除緩存而是直接更新緩存試試看。數據庫

考慮併發操做:線程A寫,線程B寫

先更新緩存再更新數據庫2

  • 一、線程A發起一個寫操做,第一步set cache
  • 二、線程B發起一個寫操做,第一步set cache
  • 三、線程B寫入數據到數據庫
  • 四、線程A寫入數據到數據庫

這樣之後每次從緩存中讀到的都是線程B設置數據,但數據庫中存儲的是線程A寫入的數據,致使數據不一致。緩存

小結

可看到先操做緩存不管是先刪除緩存仍是先更新緩存都會發生數據不一致的狀況,因此不推薦兩種作法。併發

先更新數據庫,再更新緩存

考慮併發操做:線程A寫,線程B讀

先更新數據庫再更新緩存1

  • 一、線程A發起一個寫操做,第一步寫入數據到數據庫
  • 二、線程A第二步delete cache
  • 三、線程B發起一個讀操做,cache miss
  • 四、線程B從數據庫獲取最新數據
  • 五、線程B同時set cache

注意咱們的更新是先更新數據庫,成功後,讓緩存失效。ide

一個是查詢操做,一個是更新操做的併發,首先,沒有了文章開始刪除cache數據的操做了,而是先更新了數據庫中的數據,此時,緩存依然有效,因此,併發的查詢操做拿的是沒有更新的數據,可是,更新操做立刻讓緩存的失效了,後續的查詢操做再把數據從數據庫中拉出來。而不會像文章開頭的那個邏輯產生的問題,後續的查詢操做一直都在取老的數據。post

這是標準的design pattern,包括Facebook的論文《Scaling Memcache at Facebook》也使用了這個策略。爲何不是寫完數據庫後更新緩存?你能夠看一下Quora上的這個問答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕兩個併發的寫操做致使髒數據,這個問題咱們下面會說到。.net

考慮併發操做:線程A寫,線程B寫,線程C讀

先更新數據庫再更新緩存2

  • 一、線程A發起一個寫操做,第一步寫入數據到數據庫
  • 二、線程B發起一個寫操做,第一步寫入數據到數據庫
  • 三、線程B第二步delete cache
  • 四、線程C發起一個讀操做,cache miss
  • 五、線程C從數據庫獲取最新數據
  • 六、線程C同時set cache
  • 七、線程A第二步delete cache

該狀況下因爲線程A、B最初都把數據寫入了數據庫,接着都有delete cache,此時若是有線程C來讀數據,你會發現無論線程C的動做作任意順序穿插在A、B動做之間,最後查詢數據最差也就是在線程A、B刪除cache以前獲取到了舊數據,其他都會獲取到新數據,並不會影響後來的請求獲取到新數據。線程

爲何最後是把緩存的數據刪掉,而不是把更新的數據寫到緩存裏。這麼作引起的問題是,若是A、B兩個線程同時作數據更新,A先更新了數據庫,B後更新數據庫,則此時數據庫裏存的是B的數據。而更新緩存的時候,是B先更新了緩存,而A後更新了緩存,則緩存裏是A的數據。這樣緩存和數據庫的數據會發生不一致。3d

小結

可看到:

  • 先操做數據庫再刪除緩存能有不錯的結果(最差是操做未結束時會獲取到舊數據,不會影響後續請求),因此最推薦這種作法。
  • 先操做數據庫再更新緩存可能形成數據不一致的場景,不推薦這種作法。

參考

相關文章
相關標籤/搜索