詳解讓MySQL和Redis數(shù)據(jù)保持一致的四種策略
1 前言
先闡明一下 MySQL 和 Redis 的關(guān)系:MySQL 是數(shù)據(jù)庫,用來持久化數(shù)據(jù),一定程度上保證數(shù)據(jù)的可靠性;Redis 是用來當緩存,用來提升數(shù)據(jù)訪問的性能。
關(guān)于如何保證 MySQL 和 Redis 中的數(shù)據(jù)一致(即緩存一致性問題),這是一個非常經(jīng)典的問題。
使用過緩存的人都應(yīng)該知道,在實際應(yīng)用場景中,要想實時刻保證緩存和數(shù)據(jù)庫中的數(shù)據(jù)一樣,很難做到。
基本上都是盡可能讓他們的數(shù)據(jù)在絕大部分時間內(nèi)保持一致,并保證最終是一致的。
1.1 緩存不一致是如何產(chǎn)生的
如果數(shù)據(jù)一直沒有變更,那么就不會出現(xiàn)緩存不一致的問題。
通常緩存不一致是發(fā)生在數(shù)據(jù)有變更的時候。因為每次數(shù)據(jù)變更你需要同時操作數(shù)據(jù)庫和緩存,而他們又屬于不同的系統(tǒng),無法做到同時操作成功或失敗,總會有一個時間差。在并發(fā)讀寫的時候可能就會出現(xiàn)緩存不一致的問題(理論上通過分布式事務(wù)可以保證這一點,不過實際上基本上很少有人這么做)。
雖然沒辦法在數(shù)據(jù)有變更時,保證緩存和數(shù)據(jù)庫強一致,但對緩存的更新還是有一定設(shè)計方法的,遵循這些設(shè)計方法,能夠讓這個不一致的影響時間和影響范圍最小化。
1.2 緩存更新的幾種設(shè)計
緩存更新的設(shè)計方法大概有以下四種:
先刪除緩存,再更新數(shù)據(jù)庫(這種方法在并發(fā)下最容易出現(xiàn)長時間的臟數(shù)據(jù),不可?。?/p>
先更新數(shù)據(jù)庫,刪除緩存(Cache Aside Pattern)
只更新緩存,由緩存自己同步更新數(shù)據(jù)庫(Read/Write Through Pattern)
只更新緩存,由緩存自己異步更新數(shù)據(jù)庫(Write Behind Cache Pattern)
接下來詳細介紹一些這四種設(shè)計方法
2 設(shè)計方法一:先刪除緩存,再更新數(shù)據(jù)庫
這種方法在并發(fā)讀寫的情況下容易出現(xiàn)緩存不一致的問題
如上圖所示,其可能的執(zhí)行流程順序為:
- 客戶端1 觸發(fā)更新數(shù)據(jù)A的邏輯
- 客戶端2 觸發(fā)查詢數(shù)據(jù)A的邏輯
- 客戶端1 刪除緩存中數(shù)據(jù)A
- 客戶端2 查詢緩存中數(shù)據(jù)A,未命中
- 客戶端2 從數(shù)據(jù)庫查詢數(shù)據(jù)A,并更新到緩存中
- 客戶端1 更新數(shù)據(jù)庫中數(shù)據(jù)A
可見,最后緩存中的數(shù)據(jù) A 跟數(shù)據(jù)庫中的數(shù)據(jù) A 是不一致的,緩存中的數(shù)據(jù)A是舊的臟數(shù)據(jù)。
因此一般不建議使用這種方式。
3 設(shè)計方法二:先更新數(shù)據(jù)庫,再讓緩存失效
這種方法在并發(fā)讀寫的情況下,也可能會出現(xiàn)短暫緩存不一致的問題
如上圖所示,其可能執(zhí)行的流程順序為:
- 客戶端1 觸發(fā)更新數(shù)據(jù)A的邏輯
- 客戶端2 觸發(fā)查詢數(shù)據(jù)A的邏輯
- 客戶端3 觸發(fā)查詢數(shù)據(jù)A的邏輯
- 客戶端1 更新數(shù)據(jù)庫中數(shù)據(jù)A
- 客戶端2 查詢緩存中數(shù)據(jù)A,命中返回(舊數(shù)據(jù))
- 客戶端1 讓緩存中數(shù)據(jù)A失效
- 客戶端3 查詢緩存中數(shù)據(jù)A,未命中
- 客戶端3 查詢數(shù)據(jù)庫中數(shù)據(jù)A,并更新到緩存中
可見,最后緩存中的數(shù)據(jù)A和數(shù)據(jù)庫中的數(shù)據(jù) A 是一致的,理論上可能會出現(xiàn)一小段時間數(shù)據(jù)不一致,不過這種概率也比較低,大部分的業(yè)務(wù)也不會有太大的問題。
4 設(shè)計方法三:只更新緩存,由緩存自己同步更新數(shù)據(jù)庫(Read/Write Through Pattern)
只更新緩存,由緩存自己同步更新數(shù)據(jù)庫(Read/Write Through Pattern)
如上圖所示,其可能執(zhí)行的流程順序為:
- 客戶端1 觸發(fā)更新數(shù)據(jù) A 的邏輯
- 客戶端2 觸發(fā)查詢數(shù)據(jù) A 的邏輯
- 客戶端1 更新緩存中數(shù)據(jù) A,緩存同步更新數(shù)據(jù)庫中數(shù)據(jù) A,再返回結(jié)果
- 客戶端2 查詢緩存中數(shù)據(jù) A,命中返回
Read Through 和 WriteThrough 的流程類似,只是在客戶端查詢數(shù)據(jù)A時,如果緩存中數(shù)據(jù)A失效了(過期或被驅(qū)逐淘汰),則緩存會同步去數(shù)據(jù)庫中查詢數(shù)據(jù)A,并緩存起來,再返回給客戶端。
這種方式緩存不一致的概率極低,只不過需要對緩存進行專門的改造。
5 只更新緩存,由緩存自己異步更新數(shù)據(jù)庫(Write Behind Cache Pattern)
這種方式性詳單于是業(yè)務(wù)只操作更新緩存,再由緩存異步去更新數(shù)據(jù)庫,例如:
如上圖所示,其可能的執(zhí)行流程順序為:
- 客戶端1 觸發(fā)更新數(shù)據(jù) A 的邏輯
- 客戶端2 觸發(fā)查詢數(shù)據(jù) A 的邏輯
- 客戶端1 更新緩存中的數(shù)據(jù) A,返回
- 客戶端2 查詢緩存中的數(shù)據(jù) A,命中返回
- 緩存異步更新數(shù)據(jù) A 到數(shù)據(jù)庫中
這種方式的優(yōu)勢是讀寫的性能都非常好,基本上只要操作完內(nèi)存后就返回給客戶端了,但是其是非強一致性,存在丟失數(shù)據(jù)的情況。
如果在緩存異步將數(shù)據(jù)更新到數(shù)據(jù)庫中時,緩存服務(wù)掛了,此時未更新到數(shù)據(jù)庫中的數(shù)據(jù)就丟失了。
6 小結(jié)
上面講到的幾種緩存更新的設(shè)計方式,都是前人總結(jié)出來的經(jīng)驗,這些方式或多或少都有一些弊端,并不完美,實際上也很難有完美的設(shè)計。大家在做系統(tǒng)設(shè)計的時候,也不要去追求完美,要有一些取舍,找到一種最適合自己業(yè)務(wù)場景的方式就行。
到此這篇關(guān)于讓MySQL和Redis數(shù)據(jù)保持一致的四種策略的文章就介紹到這了,更多相關(guān)MySQL和Redis數(shù)據(jù)保持一致內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 獲取當前日期函數(shù)及時間格式化參數(shù)詳解
這篇文章主要介紹了mysql 獲取當前日期函數(shù)now()及時間格式化DATE_FROMAT函數(shù)以及參數(shù)詳細介紹,需要的朋友可以參考下2014-08-08mysql數(shù)據(jù)庫如何導(dǎo)入導(dǎo)出sql文件
這篇文章主要介紹了mysql數(shù)據(jù)庫如何導(dǎo)入導(dǎo)出sql文件問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11MySQL主從配置及haproxy和keepalived搭建過程解析
這篇文章主要介紹了MySQL主從配置及haproxy和keepalived搭建,本次運行環(huán)境是在docker中,也會介紹一些docker的知識,需要的朋友可以參考下2022-05-05mysql 8.0 錯誤The server requested authentication method unkno
在本篇文章里小編給大家整理的是關(guān)于mysql 8.0 錯誤The server requested authentication method unknown to the client解決方法,有此需要的朋友們可以學(xué)習(xí)下。2019-08-08使用Canal監(jiān)聽MySQL Binlog日志的實現(xiàn)方案
本文檔探討了在分布式系統(tǒng)中處理超時未支付訂單的挑戰(zhàn)與解決方案,文檔還詳細介紹了MySQL Binlog的配置、Canal中間件的部署與配置,以及消息監(jiān)聽處理的實現(xiàn),確保了方案的可操作性,需要的朋友可以參考下2024-12-12