當前位置: 妍妍網 > 碼農

4 種策略讓 MySQL 和 Redis 數據保持一致

2024-03-12碼農

先闡明一下 MySQL 和 Redis 的關系: MySQL 是資料庫,用來持久化數據,一定程度上保證數據的可靠性; Redis 是用來當緩存,用來提升數據存取的效能。

關於如何保證 MySQL 和 Redis 中的數據一致(即緩存一致性問題),這是一個非常經典的問題。

使用過緩存的人都應該知道,在實際套用場景中,要想即時刻保證緩存和資料庫中的數據一樣,很難做到。

基本上都是盡可能讓他們的數據在絕大部份時間內保持一致,並保證最終是一致的。

1、緩存不一致是如何產生的

如果數據一直沒有變更,那麽就不會出現緩存不一致的問題。

通常緩存不一致是發生在數據有變更的時候。因為每次數據變更你需要同時操作資料庫和緩存,而他們又屬於不同的系統,無法做到同時操作成功或失敗,總會有一個時間差。在並行讀寫的時候可能就會出現緩存不一致的問題(理論上透過分布式事務可以保證這一點,不過實際上基本上很少有人這麽做)。

雖然沒辦法在數據有變更時,保證緩存和資料庫強一致,但對緩存的更新還是有一定設計方法的,遵循這些設計方法,能夠讓這個不一致的影響時間和影響範圍最小化。

2、緩存更新的幾種設計

緩存更新的設計方法大概有以下四種:

  • 先刪除緩存,再更新資料庫(這種方法在並行下最容易出現長時間的臟數據,不可取)

  • 先更新資料庫,刪除緩存(Cache Aside Pattern)

  • 只更新緩存,由緩存自己同步更新資料庫(Read/Write Through Pattern)

  • 只更新緩存,由緩存自己異步更新資料庫(Write Behind Cache Pattern)

  • 接下來詳細介紹一些這四種設計方法

    2.1 先刪除緩存,再更新資料庫

    這種方法在並行讀寫的情況下容易出現緩存不一致的問題

    如上圖所示,其可能的執行流程順序為:

  • 客戶端1 觸發更新數據A的邏輯

  • 客戶端2 觸發查詢數據A的邏輯

  • 客戶端1 刪除緩存中數據A

  • 客戶端2 查詢緩存中數據A,未命中

  • 客戶端2 從資料庫查詢數據A,並更新到緩存中

  • 客戶端1 更新資料庫中數據A

  • 可見,最後緩存中的數據 A 跟資料庫中的數據 A 是不一致的,緩存中的數據A是舊的臟數據。

    因此一般不建議使用這種方式。

    2.2 先更新資料庫,再讓緩存失效

    這種方法在並行讀寫的情況下,也可能會出現短暫緩存不一致的問題

    如上圖所示,其可能執行的流程順序為:

  • 客戶端1 觸發更新數據A的邏輯

  • 客戶端2 觸發查詢數據A的邏輯

  • 客戶端3 觸發查詢數據A的邏輯

  • 客戶端1 更新資料庫中數據A

  • 客戶端2 查詢緩存中數據A,命中返回(舊數據)

  • 客戶端1 讓緩存中數據A失效

  • 客戶端3 查詢緩存中數據A,未命中

  • 客戶端3 查詢資料庫中數據A,並更新到緩存中

  • 可見,最後緩存中的數據A和資料庫中的數據 A 是一致的,理論上可能會出現一小段時間數據不一致,不過這種機率也比較低,大部份的業務也不會有太大的問題。

    2.3 只更新緩存,由緩存自己同步更新資料庫(Read/Write Through Pattern)

    這種方法相當於是業務只更新緩存,再由緩存去同步更新資料庫。一個Write Through的 例子如下:

    如上圖所示,其可能執行的流程順序為:

  • 客戶端1 觸發更新數據 A 的邏輯

  • 客戶端2 觸發查詢數據 A 的邏輯

  • 客戶端1 更新緩存中數據 A,緩存同步更新資料庫中數據 A,再返回結果

  • 客戶端2 查詢緩存中數據 A,命中返回

  • Read Through 和 WriteThrough 的流程類似,只是在客戶端查詢數據A時,如果緩存中數據A失效了(過期或被驅逐淘汰),則緩存會同步去資料庫中查詢數據A,並緩存起來,再返回給客戶端。

    這種方式緩存不一致的機率極低,只不過需要對緩存進行專門的改造。

    2.4 只更新緩存,由緩存自己異步更新資料庫(Write Behind Cache Pattern)

    這種方式性詳單於是業務只操作更新緩存,再由緩存異步去更新資料庫,例如:

    如上圖所示,其可能的執行流程順序為:

  • 客戶端1 觸發更新數據 A 的邏輯

  • 客戶端2 觸發查詢數據 A 的邏輯

  • 客戶端1 更新緩存中的數據 A,返回

  • 客戶端2 查詢緩存中的數據 A,命中返回

  • 緩存異步更新數據 A 到資料庫中

  • 這種方式的優勢是讀寫的效能都非常好,基本上只要操作完記憶體後就返回給客戶端了,但是其是非強一致性,存在遺失數據的情況。

    如果在緩存異步將數據更新到資料庫中時,緩存服務掛了,此時未更新到資料庫中的數據就遺失了。

    總結

    上面講到的幾種緩存更新的設計方式,都是前人總結出來的經驗,這些方式或多或少都有一些弊端,並不完美,實際上也很難有完美的設計。大家在做系統設計的時候,也不要去追求完美,要有一些取舍,找到一種最適合自己業務場景的方式就行。

    來源:developer.jdcloud.com/article/2776

    >>

    END

    精品資料,超贊福利,免費領

    微信掃碼/長按辨識 添加【技術交流群

    群內每天分享精品學習資料

    最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎並行JVMMySQLRedisSpringSpringMVCSpringBootSpringCloud訊息佇列等多個型別),歡迎您的使用。

    👇👇

    👇點選"閱讀原文",獲取更多資料(持續更新中