MySQL處理DB讀寫分離數(shù)據(jù)不一致問題的方案
前言
在互聯(lián)網(wǎng)中大型項(xiàng)目中,讀寫分離應(yīng)該是我們小伙伴經(jīng)常聽說的,這個(gè)主要解決大流量請求時(shí),提高系統(tǒng)的吞吐量。因?yàn)榻^大部分互聯(lián)網(wǎng)產(chǎn)品都是讀多寫少,大部分都是讀請求,很小部分是寫請求。

1)一個(gè)主庫負(fù)責(zé)寫請求,更新數(shù)據(jù)
2)兩個(gè)從庫負(fù)責(zé)讀請求,可以提高系統(tǒng)吞吐量
3)主庫和從庫之間同步數(shù)據(jù)
為什么產(chǎn)生數(shù)據(jù)不一致

上圖中業(yè)務(wù)流程
1)寫請求A進(jìn)行數(shù)據(jù)更新,但寫庫還沒有來得及把更新的數(shù)據(jù)更新到讀庫
2)讀請求B進(jìn)行數(shù)據(jù)查詢,請求B是訪問的讀庫,獲取的是舊值
3)因?yàn)閷憥旌妥x庫之間存在同步延遲,導(dǎo)致數(shù)據(jù)在不同庫中不一致
讀寫庫數(shù)據(jù)不一致問題我們?nèi)绾谓鉀Q?
方案一:利用數(shù)據(jù)庫自身特性
我們一般用的數(shù)據(jù)庫是mysql和oracle,mysql是我們互聯(lián)網(wǎng)項(xiàng)目都會(huì)用到的,oracle一般大公司用的比較多(很貴啊)。
我們分析一下問題,原因就是在主庫(寫庫)與從庫(讀庫)之間數(shù)據(jù)同步延遲導(dǎo)致,mysql中有全同步復(fù)制機(jī)制、半同步復(fù)制、異步復(fù)制三種復(fù)制方案(小伙伴可以自行去了解)。
mysql全同步復(fù)制

全同步復(fù)制,當(dāng)A提交更新請求主庫事務(wù)之后,不是立即返回,而是等到所有的從庫節(jié)點(diǎn)必須收到、APPLY并且提交這些事務(wù),主庫線程才返回請求A結(jié)果,才能做后續(xù)操作。這樣就解決了數(shù)據(jù)同步延遲的問題。
問題:但這個(gè)同步方案嚴(yán)重的問題就是寫請求耗時(shí)會(huì)很長,而且會(huì)隨者從庫數(shù)量增加,耗時(shí)也會(huì)增加。(不推薦)
oracle共享存儲

上圖采用了oracle RAC方案,DB服務(wù)其實(shí)就代表一個(gè)應(yīng)用服務(wù),所有的數(shù)據(jù)存儲在同一個(gè)地方,所有就不存在數(shù)據(jù)同步這個(gè)問題。當(dāng)然這個(gè)部署方案不是我們嚴(yán)格意義上面的讀寫分離,存儲是獨(dú)立的。
方案二:不解決
我們設(shè)計(jì)任何架構(gòu)方案,都要圍繞著業(yè)務(wù),如果業(yè)務(wù)能夠接受可以不解決;其實(shí)很多互聯(lián)網(wǎng)產(chǎn)品都有短時(shí)間的數(shù)據(jù)不一致問題。如:58同城,美團(tuán),貼吧等。
但有些場景是不允許的。如

上圖中:
1)用戶寫了一篇文章,點(diǎn)擊保存按鈕
2)系統(tǒng)執(zhí)行保存方法,提示用戶保存成功
3)保存成功后一般系統(tǒng)就會(huì)立即跳轉(zhuǎn)到文章列表,按照時(shí)間倒序,最新的文章排在第一個(gè),這個(gè)業(yè)務(wù)是很正常的,讓用戶可以看到自己的文章列表
4)這樣就是調(diào)用獲取文章列表的方法getArticleList,但這個(gè)方法是讀請求,走的是從庫。
5)如果出現(xiàn)主庫和從庫同步延遲,就出現(xiàn)了不一致。
方案三:客戶端保存法
這個(gè)方案是:一些業(yè)務(wù)的操作是有前端頁面的,不管是網(wǎng)頁或App等。此方案的思路就是把之前保存的文章緩存到客戶端,在用戶到文章列表時(shí),數(shù)據(jù)的組成就是(客戶端緩存文章 + 后端讀庫返回的文章數(shù)據(jù))??蛻舳艘龅木褪蔷彺嬉O(shè)置一個(gè)時(shí)間(這個(gè)緩存時(shí)間,可以預(yù)估主庫同步到從庫的時(shí)間延遲);以及要做文章去重,防止讀庫已經(jīng)同步完成,客戶端緩存沒有過期。
問題:客戶端邏輯復(fù)雜;客戶端有緩存數(shù)據(jù)大小的限制,不能保存大數(shù)據(jù)。列表分頁處理復(fù)雜。
方案四:緩存標(biāo)記法

上圖流程:
1)A發(fā)起寫請求,更新了主庫,但在緩存中設(shè)置一個(gè)標(biāo)記,代表此數(shù)據(jù)已經(jīng)更新,標(biāo)記格式(業(yè)務(wù)代號:數(shù)據(jù)庫:表:主鍵ID)根據(jù)自己業(yè)務(wù)場景。
2)設(shè)置此標(biāo)記,要加上過期時(shí)間,可以為預(yù)估的主庫和從庫同步延遲的時(shí)間
3)B發(fā)起讀請求的時(shí)候,先判斷此請求的業(yè)務(wù)在緩存中有沒有更新標(biāo)記
4)如果存在標(biāo)記,走主庫;如果沒有走從庫。
這個(gè)方案就有效了解決了數(shù)據(jù)不一致的問題。
但這個(gè)方案會(huì)有個(gè)嚴(yán)重的問題,也就是每次的讀請求都要到緩存中去判斷是否存在緩存標(biāo)記,如果是單機(jī)部署用的是jvm緩存,對性能還好;但如果是集群部署緩存肯定用redis,每次讀都要和redis進(jìn)行交互,這樣肯定會(huì)影響系統(tǒng)吞吐量。
那怎么辦?怎么辦?繼續(xù)往下看
方案五:本地緩存標(biāo)記

上圖流程:
1)用戶A發(fā)起寫請求,更新了主庫,并在客戶端設(shè)置標(biāo)記,過期時(shí)間,如:cookies
2)用戶A再發(fā)起讀請求時(shí),帶上這個(gè)本地標(biāo)記在后端
3)后端在處理請求時(shí),獲取請求傳過來的數(shù)據(jù),看有沒有這個(gè)標(biāo)記(如:cookies)
4)有這個(gè)業(yè)務(wù)標(biāo)記,走主庫;沒有走從庫。
這個(gè)方案就保證了用戶A的讀請求肯定是數(shù)據(jù)一致的,而且沒有性能問題,因?yàn)闃?biāo)記是本地客戶端傳過去的。
但有寫小伙伴就會(huì)問那其他用戶在本地客戶端是沒有這個(gè)標(biāo)記的,他們走的就是從庫了。那其他用戶不就看不到這個(gè)數(shù)據(jù)了嗎?說的對,其他用戶是看不到,但看不到的時(shí)間很短,過個(gè)1~10秒就能夠看到。
但這個(gè)方案解決了當(dāng)前用戶的數(shù)據(jù)一致性的問題,如上面舉的例子,寫文章,然后到文章列表,本用戶是能夠看到的。其他用戶暫時(shí)看不到是沒有關(guān)系的。還是那句話,脫離業(yè)務(wù)的方案是耍流氓。(推薦)
那DB讀寫分離情況下,如何解決緩存和數(shù)據(jù)庫不一致性問題呢?
方案一:延遲消息
其實(shí)在真實(shí)業(yè)務(wù)中,尤其互聯(lián)網(wǎng)項(xiàng)目中,數(shù)據(jù)短時(shí)間的不一致時(shí)能夠接受的。就像怎么解決DB讀寫分離,導(dǎo)致數(shù)據(jù)不一致問題?中提到的本地緩存標(biāo)記法,保證了本用戶數(shù)據(jù)一致,其他用戶可暫時(shí)不一致,但最終是一致的這個(gè)思路。我們可以設(shè)置一個(gè)延遲消息,如下圖

流程:
1)在訂閱到binlog更新日志時(shí),先不刪除緩存,而是投遞一個(gè)延遲消息(如:延遲10秒的消息,就是過10秒此消息才會(huì)被消費(fèi)者監(jiān)聽到,從而被消費(fèi))
2)延遲消息的延遲時(shí)間,設(shè)置為主庫與從庫的數(shù)據(jù)同步延遲的時(shí)間,可自行預(yù)估
3)監(jiān)聽到延遲消息,在刪除緩存。
這個(gè)方案的特點(diǎn)就是讀請求會(huì)在延遲時(shí)間內(nèi)讀取到的是舊值,等到延遲時(shí)間一過,取到的就是新值。這個(gè)業(yè)務(wù)在互聯(lián)網(wǎng)產(chǎn)品中是允許的。
如果要保證本用戶(更新數(shù)據(jù)的用戶)一定讀到的是新值,這邊可以采用本地緩存標(biāo)記方案,直接從主數(shù)據(jù)庫讀取,讀取到數(shù)據(jù)后,可以把新值設(shè)置到緩存中,這樣就保證了數(shù)據(jù)一致性。
方案二:更新用戶再次發(fā)起讀請求
在方案一中,其他用戶的讀請求會(huì)有暫時(shí)間讀取到的是舊值,如何縮短時(shí)間?其實(shí)是有一個(gè)方案,就是讓更新用戶再次發(fā)起讀請求,也就是在方案一最后提到的
1)更新用戶再次發(fā)起讀請求,根據(jù)本地緩存標(biāo)記,直接走主數(shù)據(jù)庫,讀取的肯定是新值,
2)再把這個(gè)新值設(shè)置到緩存中。這樣就保證了緩存中的是新值,雖然從庫還沒有不同完成,但緩存中已經(jīng)是新值了。
3)最后從庫同步數(shù)據(jù)完成,值就達(dá)到了一致性
以上就是MySQL處理DB讀寫分離數(shù)據(jù)不一致問題的方案的詳細(xì)內(nèi)容,更多關(guān)于MySQL DB讀寫分離數(shù)據(jù)不一致的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql數(shù)據(jù)庫limit的四種用法小結(jié)
mysql數(shù)據(jù)庫中l(wèi)imit子句可以被用于強(qiáng)制select語句返回指定的記錄數(shù),本文主要介紹了mysql數(shù)據(jù)庫limit的四種用法小結(jié),感興趣的可以了解一下2023-10-10
docker下mysql 8.0.20 安裝配置方法圖文教程
這篇文章主要介紹了docker下mysql 8.0.20 安裝配置方法圖文教程,文中安裝步驟介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
MySQL Workbench工具導(dǎo)出導(dǎo)入數(shù)據(jù)庫方式
這篇文章主要介紹了MySQL Workbench工具導(dǎo)出導(dǎo)入數(shù)據(jù)庫方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
MYSQL IN 與 EXISTS 的優(yōu)化示例介紹
當(dāng)B表的數(shù)據(jù)集必須小于A表的數(shù)據(jù)集時(shí),用in優(yōu)于exists,當(dāng)A表的數(shù)據(jù)集系小于B表的數(shù)據(jù)集時(shí),用exists優(yōu)于in2014-08-08

