分布式系統(tǒng)技術(shù)原理 - 一致性(1)

最近工作中江掩,在解決配送核心系統(tǒng)的一個(gè)熱點(diǎn)問(wèn)題。為了問(wèn)題解決核畴,需要對(duì)核心業(yè)務(wù)數(shù)據(jù)庫(kù)進(jìn)行散表/拆表優(yōu)化膝但。過(guò)程中,發(fā)現(xiàn)大家對(duì)一致性問(wèn)題的認(rèn)知不完全對(duì)齊膛檀,所以想著還是有必要整理歸納一下分布式系統(tǒng)環(huán)境中一致性問(wèn)題出現(xiàn)的場(chǎng)景锰镀、原因、一致性問(wèn)題的定義及對(duì)應(yīng)方案或者說(shuō)‘套路’ --- 最近重新翻看《萬(wàn)萬(wàn)沒(méi)想到 - 用理工科思維理解世界》咖刃,其中就講到了要‘掌握套路’,通過(guò)不斷‘練習(xí)’憾筏,在頭腦中形成針對(duì)問(wèn)題及技能的‘神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)’ 嚎杨。當(dāng)碰到類似場(chǎng)景或者問(wèn)題時(shí),能夠一眼識(shí)別出‘問(wèn)題’模式氧腰、并采取對(duì)應(yīng)方案來(lái)解決)枫浙。

一致性問(wèn)題的場(chǎng)景

先來(lái)看看幾個(gè)一致性問(wèn)題的場(chǎng)景。

場(chǎng)景1 - 一個(gè)用戶通過(guò)web 服務(wù)上傳一個(gè)全尺寸圖片并保存到一個(gè)文件存儲(chǔ)當(dāng)中古拴。而一般文件存儲(chǔ)為提供數(shù)據(jù)可靠性箩帚,往往都會(huì)對(duì)上傳的內(nèi)容進(jìn)行備份。另外一個(gè)應(yīng)用-‘圖片調(diào)整模塊’通過(guò)監(jiān)聽(tīng)消息隊(duì)列中’圖片上傳‘事件黄痪,會(huì)從文件存儲(chǔ)當(dāng)中讀取文件紧帕,并進(jìn)行圖片尺寸調(diào)整,然后將調(diào)整后的圖片再保存回文件存儲(chǔ)中桅打。

這個(gè)場(chǎng)景下是嗜,可能的一致性問(wèn)題是:‘圖片調(diào)整模塊’收到有文件發(fā)布消息后,就會(huì)從文件存儲(chǔ)中嘗試讀取該消息中對(duì)應(yīng)地址的圖片挺尾。而由于文件存儲(chǔ)是分布式的多副本系統(tǒng)鹅搪,有可能會(huì)返回這個(gè)圖片的一個(gè)舊的副本,然后開(kāi)始進(jìn)行圖片調(diào)整操作遭铺,并將調(diào)整完后的圖片寫回保存丽柿。這樣一來(lái)恢准,就會(huì)使得真正需要進(jìn)行調(diào)整的新圖片被覆蓋。

相信這類場(chǎng)景甫题,在其他日常的業(yè)務(wù)/系統(tǒng)需求中馁筐,很常見(jiàn)。如上游服務(wù)創(chuàng)建了一個(gè)訂單寫入到數(shù)據(jù)庫(kù)中幔睬,然后通過(guò)MQ消息通知下游服務(wù)眯漩,并通過(guò)RPC接口來(lái)調(diào)用獲取訂單信息(一般的數(shù)據(jù)庫(kù)都采用主從集群,而服務(wù)端如不加以區(qū)分處理麻顶,則有可能讀從庫(kù)----數(shù)據(jù)延遲赦抖,而讀到舊數(shù)據(jù)),下游服務(wù)獲得后修改辅肾,然后再寫回队萤。這一類場(chǎng)景中,如果不關(guān)注一致性相關(guān)問(wèn)題矫钓,可能就會(huì)出現(xiàn)數(shù)據(jù)更新丟失的業(yè)務(wù)錯(cuò)誤要尔。

場(chǎng)景1

場(chǎng)景2 - 一個(gè)專門用于對(duì)用戶進(jìn)行運(yùn)營(yíng)獎(jiǎng)勵(lì)的業(yè)務(wù)服務(wù)A,基于產(chǎn)品同學(xué)新的產(chǎn)品需求---- “對(duì)在某個(gè)時(shí)間范圍內(nèi)購(gòu)買了某些商品且訂單金額大于某個(gè)下限值的用戶進(jìn)行發(fā)券獎(jiǎng)勵(lì)”新娜,RD基于這個(gè)需求開(kāi)發(fā)赵辕、上線。但卻忽略了商品訂單可能會(huì)被用戶退貨/申請(qǐng)退款(而這個(gè)業(yè)務(wù)場(chǎng)景很可能并不會(huì)被負(fù)責(zé)用戶運(yùn)營(yíng)的研發(fā)RD和產(chǎn)品同學(xué)知道)的場(chǎng)景概龄。那么还惠,一旦這樣的情況發(fā)生,業(yè)務(wù)服務(wù)A之前發(fā)送給用戶的獎(jiǎng)勵(lì)優(yōu)惠券的條件就相當(dāng)于失效了私杜,但優(yōu)惠券卻已經(jīng)發(fā)送了出去蚕键。更有甚者,如果被黑產(chǎn)盯上衰粹,撞到了這個(gè)漏洞锣光,就可能引發(fā)業(yè)務(wù)安全事件,惡意刷券铝耻。

這個(gè)不一致的場(chǎng)景誊爹,雖然不會(huì)造成數(shù)據(jù)上的丟失,但卻造成了業(yè)務(wù)錯(cuò)誤田篇,而且這類錯(cuò)誤往往在業(yè)務(wù)復(fù)雜的系統(tǒng)中會(huì)出現(xiàn)替废,尤其產(chǎn)品或研發(fā)負(fù)責(zé)不同的業(yè)務(wù)服務(wù)/產(chǎn)品方向,一個(gè)新的產(chǎn)品需求依賴某個(gè)對(duì)象數(shù)據(jù)而又沒(méi)有考慮之前’歷史悠久‘的’會(huì)改變這個(gè)對(duì)象數(shù)據(jù)‘的場(chǎng)景泊柬,就會(huì)出現(xiàn)這種狀態(tài)不一致的問(wèn)題椎镣。

場(chǎng)景3 - 與場(chǎng)景2 類似,但引起問(wèn)題的原因不同兽赁。 相信大家都用過(guò)Redis的分布式鎖状答,來(lái)對(duì)某些競(jìng)爭(zhēng)資源/數(shù)據(jù)狀態(tài)進(jìn)行加鎖控制冷守,以避免多服務(wù)/多線程并發(fā)進(jìn)行修改,且一個(gè)分布式鎖為了最終能被釋放惊科,除了在程序中通過(guò)在finally 代碼塊兒中顯示進(jìn)行鎖的釋放之外拍摇,都會(huì)加一個(gè)系統(tǒng)自動(dòng)過(guò)期時(shí)間,且一般較長(zhǎng)(如10 ~ 20 秒)馆截,以防止鎖不能被釋放充活、資源/數(shù)據(jù)狀態(tài)一直被占用。這樣的一個(gè)場(chǎng)景會(huì)有什么一致性的問(wèn)題呢蜡娶?

本場(chǎng)景中混卵,正常情況下不會(huì)有什么問(wèn)題。但由于分布式系統(tǒng)的不確定性窖张,就會(huì)導(dǎo)致系統(tǒng)出現(xiàn)’意料之外‘的問(wèn)題 ---- 由于系統(tǒng)依賴外部數(shù)據(jù)異常(如幕随,所需要處理的數(shù)據(jù)量突然變多)或系統(tǒng)bug,導(dǎo)致該服務(wù)GC時(shí)間特別長(zhǎng)(如前段時(shí)間親身經(jīng)歷過(guò)的線上核心服務(wù)就發(fā)生了一次超過(guò)1分鐘的GC)宿接,還沒(méi)有等到服務(wù)應(yīng)用執(zhí)行finally 代碼進(jìn)行鎖釋放赘淮,Redis 系統(tǒng)就自動(dòng)觸發(fā)了鎖過(guò)期釋放,而此時(shí)另外一個(gè)服務(wù)實(shí)例開(kāi)始搶占相同的這把’鎖‘睦霎,并開(kāi)始執(zhí)行其處理邏輯梢卸。但也就在此時(shí),從長(zhǎng)時(shí)間GC恢復(fù)過(guò)來(lái)的線程開(kāi)始執(zhí)行finally 代碼塊兒中鎖釋放操作副女,就會(huì)把剛剛被另外一個(gè)線程搶占的鎖釋放掉低剔,造成系統(tǒng)和業(yè)務(wù)錯(cuò)誤。

場(chǎng)景4 - 兩個(gè)用戶先后對(duì)數(shù)據(jù)庫(kù)的counter 字段進(jìn)行加一操作肮塞。方式是進(jìn)行‘read-modify-write’操作,由于時(shí)間差姻锁,用戶2在讀取counter 數(shù)據(jù)后進(jìn)行加一操作(而此時(shí)用戶1先于用戶2進(jìn)行了數(shù)據(jù)庫(kù)更新)枕赵,然后再寫回。這樣一來(lái)導(dǎo)致counter并沒(méi)有依次進(jìn)行累加位隶,而是覆蓋拷窜,導(dǎo)致數(shù)據(jù)丟失。

場(chǎng)景4

場(chǎng)景5 - Alice在銀行有1000美元的存款涧黄,分為兩個(gè)賬戶篮昧,每個(gè)500美元。現(xiàn)在有這樣一筆轉(zhuǎn)賬交易從其賬戶 1 轉(zhuǎn)100美元到賬戶2 笋妥。如果在她提交轉(zhuǎn)賬請(qǐng)求之后而銀行數(shù)據(jù)庫(kù)系統(tǒng)執(zhí)行轉(zhuǎn)賬的過(guò)程中間懊昨,來(lái)查看兩個(gè)賬戶的余額,她有可能會(huì)看到賬號(hào)2在收到轉(zhuǎn)賬之前的余額(500 美元)和賬戶1在完成轉(zhuǎn)賬之后的余額(400 美元)春宣。對(duì)于 Alice來(lái)說(shuō)酵颁,貌似她的賬戶總共只有900美元嫉你,有100美元消失了。

場(chǎng)景5

場(chǎng)景6 - 對(duì)于由數(shù)據(jù)庫(kù)和緩存組成的訂單系統(tǒng)(數(shù)據(jù)庫(kù)用于存儲(chǔ)訂單全量數(shù)據(jù)躏惋,緩存用于存儲(chǔ)熱點(diǎn)數(shù)據(jù))幽污,訂單每次更新,先更新數(shù)據(jù)庫(kù)再更新緩存簿姨,若緩存更新失敗則進(jìn)行重試距误。在這樣的架構(gòu)下,若發(fā)生前后兩次對(duì)訂單標(biāo)簽字段(一般數(shù)據(jù)庫(kù)中都會(huì)設(shè)計(jì)一個(gè)以json 格式存放各類擴(kuò)展信息的字段)的更新扁位,由于網(wǎng)絡(luò)抖動(dòng)致使第一次更新失敗准潭,而第二次更新成功,但第一次緩存更新在經(jīng)過(guò)重試一次后贤牛,成功寫入緩存惋鹅,但卻也覆蓋了第二次更新的數(shù)據(jù),造成數(shù)據(jù)丟失殉簸。

一致性問(wèn)題出現(xiàn)的原因

綜合上述場(chǎng)景闰集,我們可以從中歸納發(fā)生不一致性問(wèn)題的規(guī)律和原因:

(1)數(shù)據(jù)狀態(tài)由一個(gè)操作促使其發(fā)生改變,而另外一個(gè)操作讀到的數(shù)據(jù)是舊的狀態(tài)般卑,并基于舊數(shù)據(jù)進(jìn)行修改武鲁。而在這個(gè)過(guò)程中,讓另外一個(gè)操作讀取舊數(shù)據(jù)狀態(tài)的原因又可以演變成常見(jiàn)的如下幾種:

? ? 場(chǎng)景1 - 一個(gè)操作更新完成后蝠检,通過(guò)另外一個(gè)通道告知下一個(gè)操作去讀數(shù)據(jù)并操作沐鼠,而所讀數(shù)據(jù)是一個(gè)存儲(chǔ)中的舊的副本(并非原有被真正改寫的數(shù)據(jù)),由于多副本間數(shù)據(jù)更新延遲叹谁,導(dǎo)致讀取到舊數(shù)據(jù)饲梭。因此后續(xù)的操作會(huì)依據(jù)舊數(shù)據(jù)進(jìn)行修改而導(dǎo)致不一致。

? ? 場(chǎng)景2 - 一個(gè)操作讀到數(shù)據(jù)后并據(jù)此進(jìn)行后續(xù)處理焰檩,但另外一個(gè)操作改變了原有數(shù)據(jù)狀態(tài)憔涉,使得之前的操作不再符合其邏輯約束。這里依賴同一份數(shù)據(jù)的不同操作分屬于不同的業(yè)務(wù)系統(tǒng)析苫,甚至不同的時(shí)間周期兜叨,難以在系統(tǒng)間通過(guò)‘并發(fā)鎖’等方式進(jìn)行有效控制。

? ? 場(chǎng)景3 - 一個(gè)操作通過(guò)原子API獲得了鎖衩侥,但是被另外一個(gè)操作釋放国旷,數(shù)據(jù)狀態(tài)發(fā)生了改變。而第一個(gè)操作由于系統(tǒng)異常茫死,發(fā)生了意料之外的長(zhǎng)時(shí)間GC跪但,超過(guò)了預(yù)期內(nèi)釋放鎖的時(shí)間,執(zhí)行順序錯(cuò)亂璧榄,使得其從故障中恢復(fù)后再對(duì)其操作時(shí)特漩,已不是之前的那個(gè)鎖吧雹。

? ? 場(chǎng)景4 - 兩個(gè)并發(fā)的針對(duì)同一份數(shù)據(jù)的‘read-modify-write’操作是非原子性操作,使得兩個(gè)操作讀到了相同的數(shù)據(jù)并發(fā)進(jìn)行了修改涂身,從而數(shù)據(jù)被覆蓋而丟失雄卷。

(2)數(shù)據(jù)的正確狀態(tài)由多個(gè)對(duì)象(與之對(duì)應(yīng)的是單體對(duì)象/一份數(shù)據(jù))的完整性約束關(guān)系確定。而針對(duì)多個(gè)對(duì)象的多個(gè)多步操作并不能進(jìn)行很好的隔離(如數(shù)據(jù)庫(kù)的事務(wù)隔離級(jí)別)蛤售,從而導(dǎo)致整體上數(shù)據(jù)不一致丁鹉,如:

? ? 場(chǎng)景5 - 一個(gè)讀事務(wù)(分別讀取同一個(gè)用戶的兩個(gè)賬號(hào)),一個(gè)更新事務(wù)(先給賬號(hào)1加100悴能,再給賬號(hào)2減100)揣钦,每個(gè)事務(wù)都有兩步操作。但由于缺乏了類似‘快照隔離’的事務(wù)隔離機(jī)制漠酿,在讀事務(wù)中嘗試讀取第二個(gè)賬號(hào)時(shí)冯凹,讀到了晚于讀事務(wù)創(chuàng)建的更新事務(wù)提交后的數(shù)據(jù),而非快照數(shù)據(jù)炒嘲,使得數(shù)據(jù)整體上變成了900宇姚,導(dǎo)致‘不可重復(fù)讀’

? ? (注:數(shù)據(jù)庫(kù)事務(wù)隔離級(jí)別(Isolation)是數(shù)據(jù)庫(kù)事務(wù)4個(gè)屬性(特性、ACID)中的一個(gè)夫凸,主要用于保障當(dāng)有多個(gè)并發(fā)事務(wù)發(fā)生時(shí)避免竟態(tài)條件的出現(xiàn)使得數(shù)據(jù)錯(cuò)誤浑劳。而對(duì)于一致性的保障,更多的是由應(yīng)用程序通過(guò)借助數(shù)據(jù)庫(kù)的事務(wù)原子性和事務(wù)隔離型夭拌,來(lái)達(dá)到數(shù)據(jù)狀態(tài)一致性魔熏。一致性問(wèn)題不源于數(shù)據(jù)庫(kù)而是應(yīng)用程序的“預(yù)期狀態(tài)”。因此鸽扁,不同的數(shù)據(jù)庫(kù)事務(wù)級(jí)別也只能解決與其對(duì)應(yīng)的不同層次的數(shù)據(jù)一致性問(wèn)題蒜绽。如場(chǎng)景5中的‘不可重復(fù)讀’問(wèn)題可以通過(guò)設(shè)置‘快照隔離級(jí)別’來(lái)解決,而‘臟讀’和‘臟寫’問(wèn)題可以通過(guò)‘讀-提交’隔離級(jí)別來(lái)解決桶现。這里就不對(duì)‘臟讀’和‘臟寫’的場(chǎng)景進(jìn)行贅述了滓窍,其本質(zhì)上與該原因(1)解釋的‘讀到舊數(shù)據(jù)’類似,它是讀到了事務(wù)過(guò)程中的‘臨時(shí)數(shù)據(jù)’并進(jìn)行了修改)

(3)數(shù)據(jù)狀態(tài)本應(yīng)由多個(gè)操作按照邏輯正確的順序進(jìn)行改變以達(dá)到最終正確的狀態(tài)巩那,但執(zhí)行時(shí)由于異步等原因,使得順序不再一致此蜈,從而導(dǎo)致?tīng)顟B(tài)不一致即横,如場(chǎng)景6

在列舉了上述典型不一致場(chǎng)景及產(chǎn)生的原因之后,我們其實(shí)可以進(jìn)一步地總結(jié)分布式系統(tǒng)下一致性問(wèn)題產(chǎn)生的‘套路’了裆赵,并在碰到如下類似模式/場(chǎng)景的時(shí)候东囚,加以小心檢查與分析:

- 單體對(duì)象的多副本更新延遲,讀寫并發(fā)

- 針對(duì)同一數(shù)據(jù)都有依賴战授,但卻在不同業(yè)務(wù)場(chǎng)景的跨領(lǐng)域業(yè)務(wù)系統(tǒng)有變更

- 網(wǎng)絡(luò)抖動(dòng)页藻、系統(tǒng)異常假死等因素桨嫁,導(dǎo)致針對(duì)同一數(shù)據(jù)(多數(shù)據(jù))的多操作執(zhí)行順序錯(cuò)亂

- 多對(duì)象的跨業(yè)務(wù)服務(wù)/跨數(shù)據(jù)庫(kù)的操作,由于異步操作而導(dǎo)致順序錯(cuò)亂



本文先整理歸納一致性問(wèn)題出現(xiàn)的場(chǎng)景及原因份帐,下一篇?jiǎng)t重點(diǎn)在分布式系統(tǒng)中一致性問(wèn)題的定義和解決方案層面璃吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市废境,隨后出現(xiàn)的幾起案子畜挨,更是在濱河造成了極大的恐慌,老刑警劉巖噩凹,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巴元,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驮宴,警方通過(guò)查閱死者的電腦和手機(jī)逮刨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)堵泽,“玉大人修己,你說(shuō)我怎么就攤上這事÷淠眨” “怎么了箩退?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)佳谦。 經(jīng)常有香客問(wèn)我戴涝,道長(zhǎng),這世上最難降的妖魔是什么钻蔑? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任啥刻,我火速辦了婚禮,結(jié)果婚禮上咪笑,老公的妹妹穿的比我還像新娘可帽。我一直安慰自己,他們只是感情好窗怒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布映跟。 她就那樣靜靜地躺著,像睡著了一般扬虚。 火紅的嫁衣襯著肌膚如雪努隙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天辜昵,我揣著相機(jī)與錄音荸镊,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛躬存,可吹牛的內(nèi)容都是我干的张惹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼岭洲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宛逗!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起钦椭,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拧额,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后彪腔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侥锦,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年德挣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恭垦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡格嗅,死狀恐怖番挺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屯掖,我是刑警寧澤玄柏,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贴铜,受9級(jí)特大地震影響粪摘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绍坝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一徘意、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轩褐,春花似錦椎咧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拗踢,卻和暖如春地技,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秒拔。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砂缩。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓作谚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庵芭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妹懒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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