Redis 和 Mysql 數據庫數據如何保持一致性

        在高併發的業務場景下,數據庫大多數情況都是用戶併發訪問最薄弱的環節。所以,就需要使用 redis 做一個緩衝操作,讓請求先訪問到 redis,而不是直接訪問 Mysql 等數據庫。這樣可以大大緩解數據庫的壓力。具體業務流程如下:

在這裏插入圖片描述

        讀取緩存步驟一般沒有什麼問題,但是一旦涉及到數據更新:數據庫和緩存更新,就容易出現緩存和數據庫間的數據一致性問題。不管是先寫數據庫,再刪除緩存;還是先刪除緩存,再寫庫,都有可能出現數據不一致的情況。舉個例子:

        1. 如果刪除了緩存 Redis,還沒有來得及寫庫 MySQL,另一個線程就來讀取,發現緩存爲空,則去數據庫中讀取數據寫入緩存,此時緩存中爲髒數據。

        2. 如果先寫了庫,在刪除緩存前,寫庫的線程宕機了,沒有刪除掉緩存,則也會出現數據不一致情況。

        因爲寫和讀是併發的,沒法保證順序,就會出現緩存和數據庫的數據不一致的問題。如何解決?這裏給出兩個解決方案,先易後難,結合業務和技術代價選擇使用。

一、 延時雙刪策略

        在寫庫前後都進行 redis.del (key) 操作,並且設定合理的超時時間。具體步驟是:

        1)先刪除緩存

        2)再寫數據庫

        3)休眠 500 毫秒(根據具體的業務時間來定)

        4)再次刪除緩存。

        那麼,這個 500 毫秒怎麼確定的,具體該休眠多久呢?

        需要評估自己的項目的讀數據業務邏輯的耗時。這麼做的目的,就是確保讀請求結束,寫請求可以刪除讀請求造成的緩存髒數據。

        當然,這種策略還要考慮 redis 和數據庫主從同步的耗時。最後的寫數據的休眠時間:則在讀數據業務邏輯的耗時的基礎上,加上幾百 ms 即可。比如:休眠 1 秒。

二、設置緩存的過期時間

        從理論上來說,給緩存設置過期時間,是保證最終一致性的解決方案。所有的寫操作以數據庫爲準,只要到達緩存過期時間,則後面的讀請求自然會從數據庫中讀取新值然後回填緩存

        結合雙刪策略 + 緩存超時設置,這樣最差的情況就是在超時時間內數據存在不一致,而且又增加了寫請求的耗時。

三、如何寫完數據庫後,再次刪除緩存成功?

        上述的方案有一個缺點,那就是操作完數據庫後,由於種種原因刪除緩存失敗,這時,可能就會出現數據不一致的情況。這裏,我們需要提供一個保障重試的方案。

1、方案一具體流程

        (1)更新數據庫數據; 

        (2)緩存因爲種種問題刪除失敗;

        (3)將需要刪除的 key 發送至消息隊列;

        (4)自己消費消息,獲得需要刪除的 key;

        (5)繼續重試刪除操作,直到成功。

        然而,該方案有一個缺點,對業務線代碼造成大量的侵入。於是有了方案二,在方案二中,啓動一個訂閱程序去訂閱數據庫的 binlog,獲得需要操作的數據。在應用程序中,另起一段程序,獲得這個訂閱程序傳來的信息,進行刪除緩存操作。 

2、方案二具體流程

        (1)更新數據庫數據;

        (2)數據庫會將操作信息寫入 binlog 日誌當中;

        (3)訂閱程序提取出所需要的數據以及 key; 

        (4)另起一段非業務代碼,獲得該信息;

        (5)嘗試刪除緩存操作,發現刪除失敗; 

        (6)將這些信息發送至消息隊列;

        (7)重新從消息隊列中獲得該數據,重試操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章