分布式之?dāng)?shù)據(jù)庫和緩存雙寫一致性方案解析!

出處:https://www.cnblogs.com/rjzheng/p/9041659.html

一愉耙、為什么寫這篇文章贮尉?

首先,緩存由于其高并發(fā)和高性能的特性朴沿,已經(jīng)在項目中被廣泛使用猜谚。在讀取緩存方面,大家沒啥疑問赌渣,都是按照下圖的流程來進(jìn)行業(yè)務(wù)操作:

這里寫圖片描述

但是在更新緩存方面魏铅,對于更新完數(shù)據(jù)庫,是更新緩存呢坚芜,還是刪除緩存览芳?又或者是先刪除緩存,再更新數(shù)據(jù)庫鸿竖?其實大家存在很大的爭議沧竟。目前沒有一篇全面的博客铸敏,對這幾種方案進(jìn)行解析,于是博主戰(zhàn)戰(zhàn)兢兢屯仗,頂著被大家噴的風(fēng)險搞坝,寫了這篇文章搔谴。

二魁袜、文章結(jié)構(gòu)

1敦第、講解緩存更新策略峰弹;

2、對每種策略進(jìn)行缺點分析芜果;

3鞠呈、針對缺點給出改進(jìn)方案;

三右钾、正文

先做一個說明蚁吝,從理論上來說,給緩存設(shè)置過期時間舀射,是保證最終一致性的解決方案窘茁。這種方案下,我們可以對存入緩存的數(shù)據(jù)設(shè)置過期時間脆烟,所有的寫操作以數(shù)據(jù)庫為準(zhǔn)山林,對緩存操作只是盡最大努力即可。也就是說如果數(shù)據(jù)庫寫成功邢羔,緩存更新失敗驼抹,那么只要到達(dá)過期時間,則后面的讀請求自然會從數(shù)據(jù)庫中讀取新值然后回填緩存拜鹤。因此框冀,接下來討論的思路不依賴于給緩存設(shè)置過期時間這個方案。在這里敏簿,我們討論三種更新策略:

1左驾、先更新數(shù)據(jù)庫,再更新緩存极谊;
2诡右、先刪除緩存,再更新數(shù)據(jù)庫轻猖;
3帆吻、先更新數(shù)據(jù)庫,再刪除緩存咙边;

應(yīng)該沒人問我猜煮,為什么沒有先更新緩存次员,再更新數(shù)據(jù)庫這種策略!

四王带、先更新數(shù)據(jù)庫淑蔚,再更新緩存

這套方案,大家是普遍反對的愕撰,為什么呢刹衫?有如下兩點原因:

原因一、線程安全角度

同時有請求A和請求B進(jìn)行更新操作搞挣,那么會出現(xiàn):

  1. 線程A更新了數(shù)據(jù)庫带迟;

  2. 線程B更新了數(shù)據(jù)庫;

  3. 線程B更新了緩存囱桨;

  4. 線程A更新了緩存仓犬;

這就出現(xiàn)請求A更新緩存應(yīng)該比請求B更新緩存早才對,但是因為網(wǎng)絡(luò)等原因舍肠,B卻比A更早更新了緩存搀继。這就導(dǎo)致了臟數(shù)據(jù),因此不考慮翠语!

原因二叽躯、業(yè)務(wù)場景角度

有如下兩點:

(1)如果你是一個寫數(shù)據(jù)庫場景比較多,而讀數(shù)據(jù)場景比較少的業(yè)務(wù)需求啡专,采用這種方案就會導(dǎo)致险毁,數(shù)據(jù)壓根還沒讀到,緩存就被頻繁的更新们童,浪費性能畔况。

(2)如果你寫入數(shù)據(jù)庫的值,并不是直接寫入緩存的慧库,而是要經(jīng)過一系列復(fù)雜的計算再寫入緩存院领。那么抽米,每次寫入數(shù)據(jù)庫后,都再次計算寫入緩存的值,無疑是浪費性能的积蔚。顯然油额,刪除緩存更為適合悯舟。

接下來討論的就是爭議最大的患整,先刪緩存,再更新數(shù)據(jù)庫济舆。還是先更新數(shù)據(jù)庫卿泽,再刪緩存的問題。

五滋觉、先刪緩存签夭,再更新數(shù)據(jù)庫

該方案會導(dǎo)致不一致的原因是:同時有一個請求A進(jìn)行更新操作齐邦,另一個請求B進(jìn)行查詢操作。那么會出現(xiàn)如下情形:

(1)請求A進(jìn)行寫操作第租,刪除緩存措拇;

(2)請求B查詢發(fā)現(xiàn)緩存不存在;

(3)請求B去數(shù)據(jù)庫查詢得到舊值慎宾;

(4)請求B將舊值寫入緩存丐吓;

(5)請求A將新值寫入數(shù)據(jù)庫;

上述情況就會導(dǎo)致不一致的情形出現(xiàn)璧诵。而且汰蜘,如果不采用給緩存設(shè)置過期時間策略仇冯,該數(shù)據(jù)永遠(yuǎn)都是臟數(shù)據(jù)之宿。

那么,如何解決呢苛坚?采用延時雙刪策略比被!偽代碼如下:

這里寫圖片描述

轉(zhuǎn)化為中文描述就是:

(1)先淘汰緩存;

(2)再寫數(shù)據(jù)庫(這兩步和原來一樣)泼舱;

(3)休眠1秒等缀,再次淘汰緩存;

這么做娇昙,可以將1秒內(nèi)所造成的緩存臟數(shù)據(jù)尺迂,再次刪除!

5.1冒掌、那么噪裕,這個1秒怎么確定的,具體該休眠多久呢股毫?

針對上面的情形膳音,讀者應(yīng)該自行評估自己的項目的讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時。然后寫數(shù)據(jù)的休眠時間則在讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時基礎(chǔ)上铃诬,加幾百ms即可祭陷。這么做的目的,就是確保讀請求結(jié)束趣席,寫請求可以刪除讀請求造成的緩存臟數(shù)據(jù)兵志。

5.2、如果你用了MySQL的讀寫分離架構(gòu)怎么辦宣肚?

OK想罕,在這種情況下,造成數(shù)據(jù)不一致的原因如下钉寝,還是兩個請求弧呐,一個請求A進(jìn)行更新操作闸迷,另一個請求B進(jìn)行查詢操作。

(1)請求A進(jìn)行寫操作俘枫,刪除緩存腥沽;

(2)請求A將數(shù)據(jù)寫入數(shù)據(jù)庫了;

(3)請求B查詢緩存發(fā)現(xiàn)鸠蚪,緩存沒有值今阳;

(4)請求B去從庫查詢,這時茅信,還沒有完成主從同步盾舌,因此查詢到的是舊值;

(5)請求B將舊值寫入緩存蘸鲸;

(6)數(shù)據(jù)庫完成主從同步妖谴,從庫變?yōu)樾轮担?/p>

上述情形,就是數(shù)據(jù)不一致的原因酌摇。還是使用雙刪延時策略膝舅。只是,睡眠時間修改為在主從同步的延時時間基礎(chǔ)上窑多,加幾百ms仍稀。

5.3、采用這種同步淘汰策略埂息,吞吐量降低怎么辦技潘?

ok,那就將第二次刪除作為異步的千康。自己起一個線程享幽,異步刪除。這樣吧秕,寫的請求就不用沉睡一段時間后了琉闪,再返回。這么做砸彬,加大吞吐量颠毙。

5.4、第二次刪除砂碉,如果刪除失敗怎么辦蛀蜜?

這是個非常好的問題,因為第二次刪除失敗增蹭,就會出現(xiàn)如下情形滴某。還是有兩個請求,一個請求A進(jìn)行更新操作,另一個請求B進(jìn)行查詢操作霎奢,為了方便户誓,假設(shè)是單庫:

(1)請求A進(jìn)行寫操作,刪除緩存幕侠;

(2)請求B查詢發(fā)現(xiàn)緩存不存在帝美;

(3)請求B去數(shù)據(jù)庫查詢得到舊值;

(4)請求B將舊值寫入緩存晤硕;

(5)請求A將新值寫入數(shù)據(jù)庫悼潭;

(6)請求A試圖去刪除請求B寫入對緩存值,結(jié)果失敗了舞箍;ok,這也就是說舰褪。如果第二次刪除緩存失敗,會再次出現(xiàn)緩存和數(shù)據(jù)庫不一致的問題疏橄。

5.5占拍、如何解決呢?

具體解決方案软族,且看博主對第六節(jié)更新策略的解析刷喜!

六残制、先更新數(shù)據(jù)庫立砸,再刪緩存

首先,先說一下初茶。老外提出了一個緩存更新套路颗祝,名為《Cache-Aside pattern》。其中就指出:

  • 失效:應(yīng)用程序先從cache取數(shù)據(jù)恼布,沒有得到螺戳,則從數(shù)據(jù)庫中取數(shù)據(jù),成功后折汞,放到緩存中倔幼;

  • 命中:應(yīng)用程序從cache中取數(shù)據(jù),取到后返回爽待;

  • 更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中损同,成功后,再讓緩存失效鸟款;

另外膏燃,知名社交網(wǎng)站facebook也在論文《Scaling Memcache at Facebook》中提出,他們用的也是先更新數(shù)據(jù)庫何什,再刪緩存的策略组哩。

6.1、這種情況不存在并發(fā)問題么?

不是的伶贰。假設(shè)這會有兩個請求蛛砰,一個請求A做查詢操作,一個請求B做更新操作黍衙,那么會有如下情形產(chǎn)生:

(1)緩存剛好失效暴备;

(2)請求A查詢數(shù)據(jù)庫,得一個舊值们豌;

(3)請求B將新值寫入數(shù)據(jù)庫涯捻;

(4)請求B刪除緩存;

(5)請求A將查到的舊值寫入緩存望迎;

ok障癌,如果發(fā)生上述情況,確實是會發(fā)生臟數(shù)據(jù)辩尊。

6.2涛浙、然而,發(fā)生這種情況的概率又有多少呢摄欲?

發(fā)生上述情況有一個先天性條件轿亮,就是6.1中步驟(3)的寫數(shù)據(jù)庫操作比步驟(2)的讀數(shù)據(jù)庫操作耗時更短,才有可能使得步驟(4)先于步驟(5)胸墙∥易ⅲ可是,大家想想迟隅,數(shù)據(jù)庫的讀操作的速度遠(yuǎn)快于寫操作的(不然做讀寫分離干嘛但骨,做讀寫分離的意義就是因為讀操作比較快,耗資源少)智袭,因此步驟(3)耗時比步驟(2)更短奔缠,這一情形很難出現(xiàn)。

假設(shè)吼野,有人非要抬杠校哎,有強迫癥,一定要解決怎么辦瞳步?

6.3闷哆、如何解決上述并發(fā)問題?

首先谚攒,給緩存設(shè)有效時間是一種方案阳准。其次,采用策略2(先刪除緩存馏臭,再更新數(shù)據(jù)庫)里給出的異步延時刪除策略野蝇,保證讀請求完成以后讼稚,再進(jìn)行刪除操作。

6.4绕沈、還有其他造成不一致的原因么锐想?

有的,這也是緩存更新策略2(先刪除緩存乍狐,再更新數(shù)據(jù)庫)和緩存更新策略3(先更新數(shù)據(jù)庫赠摇,再刪除緩存)都存在的一個問題,如果刪緩存失敗了怎么辦浅蚪,那不是會有不一致的情況出現(xiàn)么藕帜。比如一個寫數(shù)據(jù)請求,然后寫入數(shù)據(jù)庫了惜傲,刪緩存失敗了洽故,這會就出現(xiàn)不一致的情況了。這也是緩存更新策略2(先刪除緩存盗誊,再更新數(shù)據(jù)庫)里留下的最后一個疑問时甚。

6.5、如何解決哈踱?

提供一個保障的重試機(jī)制即可荒适,這里給出兩套方案。

方案一:

流程如下所示:

這里寫圖片描述

(1)更新數(shù)據(jù)庫數(shù)據(jù)开镣;

(2)緩存因為種種問題刪除失數段堋;

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

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

(5)繼續(xù)重試刪除操作卧蜓,直到成功;

然而把敞,該方案有一個缺點弥奸,對業(yè)務(wù)線代碼造成大量的侵入。于是有了方案二奋早,在方案二中盛霎,啟動一個訂閱程序去訂閱數(shù)據(jù)庫的binlog,獲得需要操作的數(shù)據(jù)耽装。在應(yīng)用程序中愤炸,另起一段程序,獲得這個訂閱程序傳來的信息掉奄,進(jìn)行刪除緩存操作规个。

方案二:

流程如下圖所示:

這里寫圖片描述

(1)更新數(shù)據(jù)庫數(shù)據(jù);

(2)數(shù)據(jù)庫會將操作信息寫入binlog日志當(dāng)中;

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

(4)另起一段非業(yè)務(wù)代碼缤苫,獲得該信息;

(5)嘗試刪除緩存操作墅拭,發(fā)現(xiàn)刪除失敾盍帷;

(6)將這些信息發(fā)送至消息隊列谍婉;

(7)重新從消息隊列中獲得該數(shù)據(jù)舒憾,重試操作;

備注說明:上述的訂閱binlog程序在mysql中有現(xiàn)成的中間件叫canal穗熬,可以完成訂閱binlog日志的功能珍剑。至于oracle中,博主目前不知道有沒有現(xiàn)成中間件可以使用死陆。另外招拙,重試機(jī)制,博主是采用的是消息隊列的方式措译。如果對一致性要求不是很高别凤,直接在程序中另起一個線程,每隔一段時間去重試即可领虹,這些大家可以靈活自由發(fā)揮规哪,只是提供一個思路。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末塌衰,一起剝皮案震驚了整個濱河市诉稍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌最疆,老刑警劉巖杯巨,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異努酸,居然都是意外死亡服爷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門获诈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仍源,“玉大人,你說我怎么就攤上這事舔涎×龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵亡嫌,是天一觀的道長嚎于。 經(jīng)常有香客問我掘而,道長,這世上最難降的妖魔是什么匾旭? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任镣屹,我火速辦了婚禮,結(jié)果婚禮上价涝,老公的妹妹穿的比我還像新娘女蜈。我一直安慰自己,他們只是感情好色瘩,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布伪窖。 她就那樣靜靜地躺著,像睡著了一般居兆。 火紅的嫁衣襯著肌膚如雪覆山。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天泥栖,我揣著相機(jī)與錄音簇宽,去河邊找鬼。 笑死吧享,一個胖子當(dāng)著我的面吹牛魏割,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钢颂,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼钞它,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了殊鞭?” 一聲冷哼從身側(cè)響起遭垛,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎操灿,沒想到半個月后锯仪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡牲尺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年卵酪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤碳。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溢豆,靈堂內(nèi)的尸體忽然破棺而出蜒简,到底是詐尸還是另有隱情,我是刑警寧澤漩仙,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布搓茬,位于F島的核電站犹赖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏卷仑。R本人自食惡果不足惜峻村,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锡凝。 院中可真熱鬧粘昨,春花似錦、人聲如沸窜锯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锚扎。三九已至吞瞪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驾孔,已是汗流浹背芍秆。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留翠勉,地道東北人妖啥。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像眉菱,于是被迫代替她去往敵國和親迹栓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容