說這個問題以前得看下幾種緩存模式,能夠先看下緩存模式(Cache Aside、Read Through、Write Through、Write Behind)這篇文章。html
這樣之後每次從緩存中讀到的都是老數據,數據不一致,不能知足咱們的需求。shell
既然這種狀況下先刪除緩存會有數據不一致的狀況,那咱們來試試第一步不刪除緩存而是直接更新緩存試試看。數據庫
這樣之後每次從緩存中讀到的都是線程B設置數據,但數據庫中存儲的是線程A寫入的數據,致使數據不一致。緩存
可看到先操做緩存不管是先刪除緩存仍是先更新緩存都會發生數據不一致的狀況,因此不推薦兩種作法。併發
注意咱們的更新是先更新數據庫,成功後,讓緩存失效。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最初都把數據寫入了數據庫,接着都有delete cache,此時若是有線程C來讀數據,你會發現無論線程C的動做作任意順序穿插在A、B動做之間,最後查詢數據最差也就是在線程A、B刪除cache以前獲取到了舊數據,其他都會獲取到新數據,並不會影響後來的請求獲取到新數據。線程
爲何最後是把緩存的數據刪掉,而不是把更新的數據寫到緩存裏。這麼作引起的問題是,若是A、B兩個線程同時作數據更新,A先更新了數據庫,B後更新數據庫,則此時數據庫裏存的是B的數據。而更新緩存的時候,是B先更新了緩存,而A後更新了緩存,則緩存裏是A的數據。這樣緩存和數據庫的數據會發生不一致。3d
可看到: