天天看點

Redis和Mysql如何保證資料⼀緻

Redis和資料庫讀寫一緻問題,主要是由于Redis和資料庫的操作不同步,也不可能做到同步導緻的,那麼我們所能做的,也就是盡可能保證Redis中的髒資料能夠自己消除,而不是做到毫秒不差,緩存一緻問題這在計算機界都是一個難題。

我們要先說清楚,Redis的更新,是直接将原來的值删除。然後再從資料庫中取值時再存到資料庫。為什麼不直接修改Redis值呢?因為我們沒有必要資料庫一更新就去改Redis,不妨等到需要時再存到資料庫。

什麼意思呢?通俗一點,你開了一個店,裡面有各類售價。在門口也有個價格表,一開始是全空的。每次有人想你問一種商品價格,你就去價格表上補充上該商品價格。當有一件商品價格變化了,你要做的,就是直接從價格表上直接删了這個商品。而不是去價格表更新這個商品。如果這個商品一個月都沒人買,而價格每天都變,那你每天修改價格表就沒有意義。等到一個月後,有人需要這個商品,來問價格,再把此時的價格更新到價格表,一個月就改了一次價格表,也沒有耽誤任何事,是不是很好!

因為寫和讀是并發的,沒法保證順序,就會出現緩存和資料庫的資料不一緻的問題。

删除緩存,再寫資料庫。

如果删除了緩存Redis,還沒有來得及寫MySQL,另一個線程來讀取,發現緩存為空,則去資料庫中讀取資料,此時緩存中為髒資料。

場景一:有一熱門貼,樓主删除了文章,Redis将該文章删除,并修改資料庫。此時,有人通路了該文章,發現Redis不存在,去資料庫找,并更新到Redis。此時才完成資料庫的删除操作。那麼文章删除失敗,仍然在Redis中可以通路。

先寫資料庫,再删除緩存

删除緩存失敗,那麼,其他線程從緩存中讀取到的就是舊值,還是和實際資料庫中的值不同。

場景二:如更新某商品的庫存,目前商品的庫存是100,現在要更新為90,先更新資料庫更改成90,然後删除緩存,發現删除緩存失敗了,這意味着資料庫存的是90,而緩存是100,這導緻資料庫和緩存不一緻。

為緩存設定過期時間是最終保證資料一緻性的解決方案,該方案保證了任何操作都是先從資料庫入手例如場景一,文章在500ms後自動過期,那麼樓主删帖有500ms延時是完全可以接受的。場景二,100在500m後過期,期間通路Redis髒資料的情況很少甚至沒有。

一、 延時雙删政策

        1)先删除緩存

        2)再寫資料庫

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

        4)再次删除緩存。

這樣,在寫資料庫時,有其他線程讀了Mysql,把⽼資料讀到了Redis中,隻要這個時間小于500毫秒,那麼500毫秒之後,也會被删除掉,從⽽把資料保持⼀緻。也就是後發制人。

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

需要評估自己的項目的讀資料業務邏輯的耗時。這麼做的目的,就是確定讀請求結束,寫請求才删除Redis,可以删除這次讀請求造成的緩存髒資料。

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

引入了一個問題

A線程需要在更新資料庫後,還要休眠M秒再次淘汰緩存,等所有操作都執行完,這一個更新操作才真正完成,降低了更新操作的吞吐量。

解決辦法:用“異步淘汰”的政策,将休眠M秒以及二次淘汰放在另一個線程中,A線程在更新完資料庫後,可以直接傳回成功而不用等待。

二、結合雙删政策+緩存過期設定

     這樣最差的情況就是在髒資料未過期的500ms内(假設500ms過期)資料存在不一緻,而且又增加了寫資料庫請求的耗時,情況更加罕見。

三、隊列

慢着,為什麼還有三,不是已經解決了嗎?

萬一兩次删除Redis都失敗了呢?

好吧,你牛!那怎麼辦?那隻能花時間确定到底删除成功沒有了。

Redis和Mysql如何保證資料⼀緻

①更新資料庫②由于各種原因緩存删除失敗③将删除失敗的緩存放入消息隊列中④業務代碼從消息隊列中擷取需要删除的key⑤繼續嘗試删除操作,直到成功

這一方法的問題從圖上就可看出,所有事都是業務代碼做的,要大量修改業務代碼。

其他:

Redis和Mysql如何保證資料⼀緻

将資料庫操作存入binlog日志中,讀取binlog後分析 ,利用消息隊列,推送更新各台的redis緩存資料。

這樣一旦MySQL中産生了新的寫入、更新、删除等操作,就可以把binlog相關的消息推送至中間件,由中間件根據binlog中的記錄,對Redis進行删除。

其實這種機制,很類似MySQL的主從備份機制,因為MySQL的主備也是通過binlog來實作的資料一緻性。

這裡可以結合使用canal(阿裡的一款開源架構),通過該架構可以對MySQL的binlog進行訂閱,而canal正是模仿了mysql的slave資料庫的備份請求,使得Redis的資料更新達到了相同的效果。

當然,這裡的消息推送工具你也可以采用别的第三方:kafka、rabbitMQ等來實作推送更新Redis。