濮阳杆衣贸易有限公司

主頁 > 知識庫 > MySQL與Redis如何保證數(shù)據(jù)一致性詳解

MySQL與Redis如何保證數(shù)據(jù)一致性詳解

熱門標(biāo)簽:hbuilder地圖標(biāo)注 400電話申請?jiān)趺纯?/a> 隨州營銷電話機(jī)器人怎么樣 機(jī)器人電話機(jī)創(chuàng)意繪畫 杭州400電話如何申請的 高德地圖標(biāo)注商家在哪 天音通信電話機(jī)器人 江西南昌百應(yīng)電話機(jī)器人 400電話從哪里申請濱州

前言

由于緩存的高并發(fā)和高性能已經(jīng)在各種項(xiàng)目中被廣泛使用,在讀取緩存這方面基本都是一致的,大概都是按照下圖的流程進(jìn)行操作:

但是在更新緩存方面,是更新完數(shù)據(jù)庫再更新緩存還是直接刪除緩存呢?又或者是先刪除緩存再更新數(shù)據(jù)庫?在這一點(diǎn)上就值得探討了。

一致性方案

在實(shí)際項(xiàng)目開發(fā)中需要保證數(shù)據(jù)庫和緩存中的數(shù)據(jù)一致,否則人家充值了100塊,不斷刷新卻還是顯示0.01元,豈不是尷尬?從理論上來說,為緩存設(shè)置過期時(shí)間是最終保證數(shù)據(jù)一致性的解決方案,采用這種方案的話,所有的寫操作都是以數(shù)據(jù)庫為準(zhǔn),如果數(shù)據(jù)庫寫入成功但是緩存更新失敗,只要緩存到過期時(shí)間之后后面讀緩存時(shí)自然會(huì)在數(shù)據(jù)庫中讀取新的值然后更新緩存。接下來探討的思路主要的方向是在不依賴為緩存設(shè)置過期時(shí)間的前提下如何保證數(shù)據(jù)一致性。這里主要探討三種方案:

①先更新數(shù)據(jù)庫,再更新緩存
②先刪除緩存,再更新數(shù)據(jù)庫
③先更新數(shù)據(jù)庫,再刪除緩存

先更新數(shù)據(jù)庫再更新緩存

這種方案是普遍被反對的(在我的認(rèn)知范圍中~),為啥呢?為啥這種方案就被反對呢?原因主要有兩方面,請聽我細(xì)細(xì)道來:

首先從數(shù)據(jù)安全方面考慮,如果同時(shí)有請求A和請求B同時(shí)進(jìn)行操作,A先更新了數(shù)據(jù)庫的一條數(shù)據(jù),隨后B馬上有更新了該條數(shù)據(jù),但是可能因?yàn)榫W(wǎng)絡(luò)延遲等原因,B卻比A先更新了緩存,就會(huì)出現(xiàn)一種什么情況呢?緩存中的數(shù)據(jù)并不最新的B更新過的數(shù)據(jù),就導(dǎo)致了數(shù)據(jù)不一致的情況。

其次從業(yè)務(wù)場景方面考慮,如果是一個(gè)寫數(shù)據(jù)庫較多而讀數(shù)據(jù)庫較少的業(yè)務(wù),如果采用這種方案就會(huì)導(dǎo)致數(shù)據(jù)還沒讀緩存就會(huì)被頻繁更新,白白浪費(fèi)性能。

綜合以上兩方面的考慮,這種方案果斷pass。下面的兩種方案就是爭議較大的兩種方案了,到底是先刪緩存再更新數(shù)據(jù)庫還是先更新數(shù)據(jù)庫再刪除緩存?

先刪緩存再更新數(shù)據(jù)庫

如果同時(shí)有一個(gè)請求A進(jìn)行更新操作,請求B進(jìn)行查詢操作,就可能會(huì)出現(xiàn)A請求進(jìn)行寫操作前會(huì)刪除緩存,B請求剛好此時(shí)進(jìn)來發(fā)現(xiàn)緩存是空的,B請求就會(huì)查詢數(shù)據(jù)庫,如果此時(shí)A請求的寫操作還未完成,B請求查詢到的就還是舊的值,還是會(huì)將舊的值寫入緩存,A請求將新的值寫入數(shù)據(jù)庫,此時(shí)就會(huì)導(dǎo)致數(shù)據(jù)不一致的問題,如果不采用給緩存設(shè)置過期時(shí)間的策略,該數(shù)據(jù)永遠(yuǎn)都是臟數(shù)據(jù)。

解決這種情況可以采用延時(shí)雙刪的策略,就是在更新數(shù)據(jù)庫之前先刪除緩存,然后對數(shù)據(jù)庫進(jìn)行寫入操作,數(shù)據(jù)庫更新完成之后再次進(jìn)行刪除緩存的操作,目的是刪除讀請求可能造成的緩存臟數(shù)據(jù),第二次刪除緩存之前可以休眠幾秒,具體時(shí)間開發(fā)者可以評估一下自己項(xiàng)目讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時(shí),然后在該耗時(shí)基礎(chǔ)上加幾百ms即可,這么做的目的就是確保讀請求結(jié)束寫請求可以刪除讀請求造成的臟數(shù)據(jù)。如果MySQL采用的是讀寫分離的架構(gòu),可能由于主從延時(shí)的原因造成數(shù)據(jù)不一致,可以在寫操作完成之后根據(jù)主從延時(shí)時(shí)間休眠一下然后再進(jìn)行刪除緩存的操作。延時(shí)雙刪的偽代碼如下:

# 偽代碼
def delay_delete():
    redis.delete('name')  # 更新數(shù)據(jù)庫之前先刪除緩存
    sql = 'update info set name='lili' where id=1;'  # 更新數(shù)據(jù)庫
    cursor.execute(sql)  
    time.sleep(1)  # 如果mysql是主從架構(gòu)則休眠主從延時(shí)的時(shí)間再多幾百ms
    redis.delete('name')  # 再次刪除緩存

那會(huì)不會(huì)存在第二次刪除緩存失敗的情況呢?如果第二次刪除失敗,還是會(huì)造成緩存和數(shù)據(jù)庫不一致的問題,又如何解決呢?且看下一種方案。

先更新數(shù)據(jù)庫再刪除緩存

老外提出了一個(gè)緩存更新方案Cache−AsidepatternCache-Aside patternCache−Asidepattern,文章中提到**應(yīng)用程序應(yīng)該從cache中獲取數(shù)據(jù),如果獲取成功直接返回,如果沒有獲取成功,則從數(shù)據(jù)庫中獲取,成功后放到緩存中,更新數(shù)據(jù)時(shí)應(yīng)該先把數(shù)據(jù)存到數(shù)據(jù)庫中成功后再讓緩存失效。**原文如下

If an application updates information, it can follow the write-through strategy by making the modification to the data store, and by invalidating the corresponding item in the cache.
When the item is next required, using the cache-aside strategy will cause the updated data to be retrieved from the data store and added back into the cache.

這種方案會(huì)不會(huì)產(chǎn)生數(shù)據(jù)不一致的情況呢?比如下述這種情況:

有兩個(gè)請求A和B,A進(jìn)行查詢同時(shí)B進(jìn)行更新,假設(shè)發(fā)生下述情況:

①此時(shí)緩存剛好失效

②請求A 就會(huì)去查詢數(shù)據(jù)庫得到一個(gè)舊的值

③請求B將新的值寫入數(shù)據(jù)庫

④請求B寫入成功后刪除緩存

⑤請求A將查到的機(jī)制寫入緩存,產(chǎn)生臟數(shù)據(jù)...

如果發(fā)聲上述情況,確實(shí)會(huì)產(chǎn)生數(shù)據(jù)不一致的情況,但是XDM想一想,發(fā)生這種情況的概率是多少呢?如果先要產(chǎn)生這種結(jié)果,就必須有一個(gè)條件,就是請求B的操作時(shí)間非常短,短到什么程度呢,就是請求B寫入數(shù)據(jù)庫的操作要比請求A從數(shù)據(jù)庫中讀取數(shù)據(jù)的速度要快(因?yàn)閞edis非??欤虼瞬僮鱮edis的時(shí)間可以暫且忽略),只有這種情況下④才可能比⑤先發(fā)聲,但是數(shù)據(jù)庫的讀操作要遠(yuǎn)比寫操作快的多,不然做讀寫分離干嘛呢?所以這種情況發(fā)生的概率是非常非常非常的低,但是如果強(qiáng)迫癥患者出現(xiàn)必須要解決怎么辦呢?就可以采用給緩存設(shè)置過期時(shí)間或者采用第二種方案的延時(shí)雙刪策略,保證讀請求完成之后在進(jìn)行刪除操作。

最后的問題

還有問題呀,就是最終解決方案三可能 出現(xiàn)的極低概率的數(shù)據(jù)不一致的方案是采用方案二的延時(shí)雙刪策略,可是在方案二中也說了,如果出現(xiàn)緩存刪除失敗的情況咋辦?那不是還會(huì)出現(xiàn)數(shù)據(jù)不一致的問題嗎?這個(gè)問題到底如何解決呢?這里提供一個(gè)重試機(jī)制,刪除失敗就重試一次唄,這里提供一種重試的方案。

①更新數(shù)據(jù)庫
②由于各種原因緩存刪除失敗
③將刪除失敗的緩存放入消息隊(duì)列中
④業(yè)務(wù)代碼從消息隊(duì)列中獲取需要?jiǎng)h除的key
⑤繼續(xù)嘗試刪除操作,直到成功

總結(jié)

到此這篇關(guān)于MySQL與Redis如何保證數(shù)據(jù)一致性的文章就介紹到這了,更多相關(guān)MySQL與Redis數(shù)據(jù)一致性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Redis緩存常用4種策略原理詳解
  • 聊一聊Redis與MySQL雙寫一致性如何保證
  • 詳解redis緩存與數(shù)據(jù)庫一致性問題解決
  • redis實(shí)現(xiàn)分布式的方法總結(jié)
  • 面試常問:如何保證Redis緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性

標(biāo)簽:葫蘆島 保定 招商 昆明 鶴崗 常德 沈陽 石嘴山

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL與Redis如何保證數(shù)據(jù)一致性詳解》,本文關(guān)鍵詞  MySQL,與,Redis,如何,保證,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《MySQL與Redis如何保證數(shù)據(jù)一致性詳解》相關(guān)的同類信息!
  • 本頁收集關(guān)于MySQL與Redis如何保證數(shù)據(jù)一致性詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    湄潭县| 孟津县| 长海县| 舞钢市| 岱山县| 郧西县| 宁明县| 荔浦县| 浦江县| 景东| 滕州市| 东丽区| 安阳县| 卓尼县| 平邑县| 安吉县| 黄龙县| 昭苏县| 江华| 吉安县| 武平县| 常德市| 铜梁县| 嵩明县| 甘洛县| 宾阳县| 鄂尔多斯市| 襄城县| 遂川县| 兴化市| 乌审旗| 横山县| 徐州市| 河西区| 桐柏县| 同心县| 九江县| 云阳县| 丰都县| 通海县| 泸定县|