快取一致性

快取數據的一致性

讀所有的數據,首先去快取中獲取,快取中沒有就去讀資料庫,最後在快取中放一份。如果該數據在資料庫中發生改變,那麼快取裡面的數據如何和資料庫保持一致?解決這個問題,目前有兩個用的非常多的場景:

  • 雙寫模式
  • 失效模式

雙寫模式

修改資料庫的數據後再修改快取中的數據

雙寫模式

但又引出了一個問題,那就是當有並發時,執行緒一將資料庫的值改為「一號」然後立刻將這個值保存到快取,而保存過程中遇到了種種原因,如網路波動,導致了數據延遲抵達快取伺服器,而此時執行緒二已經將資料庫數據改為「二號」然後將數據保存到快取,執行緒二網路通暢,很快就抵達快取伺服器先於執行緒一將數據保存到快取中,而此時執行緒一的數據才抵達快取伺服器,將數據「一號」保存到了快取中(把二號覆蓋),這時快取中保存的就是一個臟數據。

臟數據被保存原理圖

這是臨時性的臟數據問題,但是在數據穩定、快取過期後,又能得到最新的正確數據。

讀到的最新數據有延遲,這就導致了數據的最終一致性問題。

失效模式

在改完資料庫後,將快取中的數據刪除,下一次請求進來從快取獲取數據時發現沒有對應數據,他會到資料庫中查詢並將數據保存在快取中。

失效模式

失效模式也有數據最終一致性問題,例如有三台伺服器同時並發,伺服器一負載小,修改資料庫和刪除快取一氣呵成,伺服器二負載大,在修改資料庫時磨磨蹭蹭耗時很久,而伺服器三是獲取數據的,他讀取快取沒有數據又去資料庫中將數據查出來了,此時查詢的是伺服器一修改的數據。完成這些操作後伺服器二才將資料庫修改瞬間刪除快取,而伺服器三在更新快取時遇到網路波動,非常久後才成功將數據保存到快取中,但他保存的卻是伺服器一修改的舊數據

臟數據被保存原理圖

加鎖

以上兩種模式遇到的問題(亂序)都可以通過加鎖來解決,但是加鎖後系統可能會變得笨重,所以,如果我們的數據經常修改,那麼我們還需要將其放到快取中咩?

如果數據經常修改,那麼我們的鎖會經常在,會導致整個系統非常緩慢,如果我們想要實時的讀取,數據一致性要求非常高,那麼這種情況還不如不加鎖,直接訪問資料庫。

解決方案

  • 我們發現無論是雙寫模式還是失效模式,都會導致快取的的不一致問題。即多個實例同時更新就會出現問題,應該怎麼辦?
    1. 如果是用戶緯度數據(訂單數據,用戶數據),這種並發幾率非常小,那麼就不需要考慮這個問題,快取數據加上過期時間,每隔一段時間觸發讀的主動更新即可。
    2. 如果是菜單,商品介紹等基礎數據,也可以去使用 canal 訂閱 binlog 的方式。
    3. 快取數據 + 過期時間也足夠解決大部分業務對於快取的要求。
    4. 通過加鎖保證並發讀寫,寫寫的時候按順序排隊,讀讀則無所謂。所以適合使用讀寫鎖。(業務不關心臟數據,允許臨時臟數據可忽略)。

總結

  • 我們能放入快取的數據本就不應該是實時性、一致性要求超高的,所以快取數據的時候加上過期時間,保證每天拿到當前最新數據即可。
  • 我們不應該過度設計。增加系統的複雜性
  • 遇到實時性、一致性要求高的數據,就應該查資料庫,即使慢點,也無所謂。

歡迎訪問我的個人部落格
博文在部落格中的鏈接: //www.ctong.top/index.php/archives/91