最終一致性

概念
分布式一致性問題隨處可見,任何一個實體/聯(lián)接模型贷揽,都可能存在分布式一致性問題。如果把單機(jī)拆開來看梦碗,CPU禽绪、內(nèi)存、I/O設(shè)備組成的機(jī)箱本身就是一個小型的分布式系統(tǒng)叉弦,需要確保對這個系統(tǒng)操作的最終一致性丐一。幸運的是這部分工作已經(jīng)交給操作系統(tǒng)和數(shù)據(jù)庫軟件來幫我們完成。而在大型分布式企業(yè)級應(yīng)用中淹冰,分布式最終一致性方案需要根據(jù)系統(tǒng)自身特點量身定制库车,是系統(tǒng)設(shè)計的重點。近年來隨著滬江業(yè)務(wù)的快速增長和微服務(wù)治理推廣樱拴,本地ACID事務(wù)早已不能滿足業(yè)務(wù)和系統(tǒng)的發(fā)展需求柠衍。大部分業(yè)務(wù)流程都需要跨多微服務(wù)的調(diào)用來協(xié)作完成,并且要求系統(tǒng)確保分布式最終一致性晶乔。
可以選擇分布式事務(wù)框架方案珍坊,目前主流的分布式事務(wù)框架大致可分為3類實現(xiàn) :

  • 基于XA協(xié)議的兩階段提交(2PC)方案
  • 基于支付寶最早提出的TCC(Try、Confirm正罢、Cancel)方案
  • 基于ebay最早提出的消息隊列異步確保方案
    此外還有較輕的解決方案阵漏,業(yè)務(wù)系統(tǒng)可以根據(jù)自身需要,選擇通過冪等/重試、狀態(tài)機(jī)履怯、恢復(fù)日志回还、異步校驗等技術(shù)來確保最終一致性。

重型武器

采用分布式事務(wù)框架的方案叹洲,最終一致性由分布式事務(wù)框架保證柠硕,業(yè)務(wù)程序員對框架細(xì)節(jié)完全透明。選擇這種方案运提,需要注意幾個點蝗柔。首先,由于分階段提交協(xié)議本身的脆弱性民泵,主流分階段提交協(xié)議如2PC癣丧,3PC, TCC都無法完全確保最終一致性,要采用異步校驗的手段兜底洪灯。其次坎缭,分階段提交協(xié)議帶來的高延遲,多次協(xié)議通信RTT帶來的時間損耗签钩。第三掏呼,基于消息隊列異步確保的分布式事務(wù)框架實現(xiàn),需要考慮消息可靠性和業(yè)務(wù)侵入問題铅檩。分布式事務(wù)框架也有巨大的優(yōu)勢憎夷,首先,分布式事務(wù)被框架封裝成切面昧旨,業(yè)務(wù)開發(fā)只需關(guān)心純業(yè)務(wù)拾给。其次,分布式事務(wù)的代碼開發(fā)量大大減少兔沃。對一致性和代碼質(zhì)量有極高要求的銀行蒋得、金融領(lǐng)域,分布式事務(wù)框架是最佳選擇乒疏。

輕型武器

不同于采用分布式事務(wù)框架的最終一致性方案额衙,程序員也可以選擇通過冪等/重試、狀態(tài)機(jī)怕吴、恢復(fù)日志窍侧、異步校驗等技術(shù)來確保最終一致性。這種方案不受限于平臺和框架转绷,系統(tǒng)較精簡靈活伟件,初期業(yè)務(wù)系統(tǒng)大都基于這種分布式一致性解決方案。不過這種方案對業(yè)務(wù)開發(fā)的要求更高议经,分布式一致性邏輯要業(yè)務(wù)程序員代碼實現(xiàn)斧账,容易出現(xiàn)bug谴返。
其實,主流的分布式事務(wù)框架也是通過這些基本的系統(tǒng)機(jī)制如冪等/重試其骄、狀態(tài)機(jī)亏镰、恢復(fù)日志、異步校驗等來確保的最終一致性拯爽,對比兩種方案,下文主要圍繞后一種展開論述钧忽,討論5點使系統(tǒng)達(dá)成分布式最終一致性的技術(shù)實踐毯炮。

原則

1、CAP定理

如下三屬性耸黑,任何一個聯(lián)網(wǎng)的共享數(shù)據(jù)系統(tǒng)最多只能同時滿足 2 個 :

  • 一致性(Consistency) : 每次讀取都會收到最新的寫入或錯誤
  • 可用性(Availability) : 每個請求都會收到一個不是錯誤的響應(yīng)
  • 分區(qū)容忍性(Partition tolerance) : 節(jié)點之間的網(wǎng)絡(luò)丟棄(或延遲)了任意數(shù)量的消息桃煎,系統(tǒng)仍繼續(xù)運行

由于分區(qū)容忍性是可伸縮的最基本要求,放棄分區(qū)容忍性等于放棄可伸縮大刊,所以分區(qū)容忍性是必選項为迈,大部分的分布式系統(tǒng)都是在C和A之間做選擇。需要注意的是缺菌,雖然C都叫一致性葫辐,但CAP定理中的C和數(shù)據(jù)庫事務(wù)ACID中的C是完全不同的兩個定義。

2伴郁、BASE原則

  • Basically Available : 基本可用
  • Soft state : 軟狀態(tài)
  • Eventual consistency : 最終一致性

BASE原則發(fā)展自CAP定理耿战,舍棄了系統(tǒng)的強(qiáng)一致性而選擇AP,但每個應(yīng)用可以根據(jù)自身的業(yè)務(wù)特點焊傅,采用適當(dāng)?shù)姆绞絹硎瓜到y(tǒng)達(dá)到最終一致性剂陡。用較通俗的話來描述就是 : “過程寬松,結(jié)果嚴(yán)格狐胎,你的老板不關(guān)心過程鸭栖,只看結(jié)果”。NoSQL數(shù)據(jù)庫Cassandra就是遵循的BASE原則設(shè)計握巢。不過也有分布式系統(tǒng)設(shè)計不是遵循BASE原則晕鹊,而是選擇CAP中的CP,如HBase镜粤。當(dāng)然捏题,系統(tǒng)對CAP三者的取舍并不是一成不變,可以根據(jù)實際需要改變策略肉渴。

實踐

1公荧、重試

重試機(jī)制可以使分布式不一致數(shù)據(jù)自動恢復(fù),前提是重試接口要提供冪等保證同规。重試機(jī)制是達(dá)成分布式最終一致性的重要手段循狰。例如窟社,超時重傳是TCP協(xié)議保證數(shù)據(jù)可靠性的一個重要機(jī)制,核心思想其實就是重試绪钥。\n同步重試 : 在上次請求失敗或超時灿里,程序再次發(fā)起同步調(diào)用請求。后端程序不推薦同步重試程腹,其一因為同步等待占用系統(tǒng)線程資源匣吊,其二因為重試引起的流量放大,可能導(dǎo)致系統(tǒng)雪崩寸潦。
異步重試 : 通過異步系統(tǒng)(消息隊列或調(diào)度中間件)對失敗或超時請求再次發(fā)起調(diào)用色鸳。推薦這種方式的重試,重試的時間間隔可以設(shè)置為根據(jù)重試次數(shù)指數(shù)增長见转,超過重試閾值仍未成功命雀,可以報警通知并由人工訂正。
重試也是提高系統(tǒng)可用性的一種有效手段斩箫。如果一個服務(wù)的可用性為98%(有1個9)吏砂,1次重試之后其可用性可達(dá)到99.96%(3個9),2次重試可以達(dá)到99.9992%(5個9)乘客。

2狐血、冪等

冪等的數(shù)學(xué)定義為
用通俗的話來說就是 : 相同的操作執(zhí)行多次和執(zhí)行一次產(chǎn)生的效果是一樣的。有的操作是天然冪等的寨典,如查詢氛雪、刪除操作。有的操作是人為使其冪等耸成,例如TCP的超時重傳操作就是冪等的报亩,無論客戶端將一個seq字節(jié)傳送多少次,服務(wù)端窗口只會用一次該字節(jié)井氢。冪等實現(xiàn)方式有很多 :

  • 基于記錄的悲觀鎖弦追,MySQL中通過SELECT FOR UPDATE語句實現(xiàn)。這種實現(xiàn)方式要設(shè)置AUTOCOMMIT=0花竞,加鎖和更新記錄在同一個事務(wù)中劲件,長時間鎖定記錄會降低系統(tǒng)的TPS,高并發(fā)場景不推薦使用约急。
  • 基于記錄版本號或狀態(tài)機(jī)的樂觀鎖方案零远,適用于更新數(shù)據(jù)場景。例如厌蔽,用戶下單購買一個商品的扣庫存操作實現(xiàn)冪等牵辣,可以用如下SQL語句實現(xiàn) : UPDATE stocktable SET stock = stock - 1, version = version + 1 WHERE product_id = 123 and version = 1
  • 基于數(shù)據(jù)庫唯一索引的去重表,適用于插入和更新數(shù)據(jù)的場景奴饮,由數(shù)據(jù)庫惟一索引確保多次插入和更新操作只有一次生效纬向。
  • 基于全局唯一標(biāo)識token實現(xiàn)择浊,這種方案要注意幾點 :
    1、這里校驗token是否可使用 和 置token為已使用逾条,是一個CAS原子操作琢岩,需要確保在一個原子操作中。
    2师脂、如果token存儲使用的是Redis担孔,那么驗證token的CAS操作可以使用原子自增操作incr,如果Redis值大于1則token不可使用危彩,反之可使用攒磨。還有一種實現(xiàn)方式是token生成系統(tǒng)將token預(yù)先寫入Redis,用刪除操作來校驗token是否被使用汤徽,刪除成功代表token未被使用可執(zhí)行操作。
    3灸撰、如果token存儲使用的是MySQL谒府,根據(jù)token分庫分表和建惟一索引,同時通過insert語句來判斷token是否存在浮毯,如果insert失敗則token不可使用完疫,反之可使用。

3债蓝、狀態(tài)機(jī)

狀態(tài)機(jī)是表示實體的狀態(tài)根據(jù)條件轉(zhuǎn)移的數(shù)學(xué)模型壳鹤。通過狀態(tài)機(jī)模型,系統(tǒng)可以判斷當(dāng)前不一致狀態(tài)饰迹,以及如何校正不一致狀態(tài)到一致狀態(tài)芳誓。這樣說可能比較抽象,我們拿發(fā)微信群紅包的例子來說明啊鸭。當(dāng)你點開發(fā)紅包按鈕锹淌,輸入總金額、紅包個數(shù)赠制、標(biāo)題赂摆,點擊支付成功后。其實根據(jù)時間先后紅包系統(tǒng)后臺至少經(jīng)歷過這樣一個狀態(tài)機(jī) :
1钟些、當(dāng)輸入總金額烟号、紅包個數(shù)、標(biāo)題點擊提交政恍,首先后臺創(chuàng)建一個初始化狀態(tài)(INIT)紅包
2汪拥、接著系統(tǒng)將根據(jù)你輸入的總金額和個數(shù)n將紅包拆分成n分,此時紅包的狀態(tài)為拆分成功(SPLITTED)
3抚垃、此時紅包后臺會監(jiān)聽異步支付消息喷楣,如果支付成功則將紅包置為支付成功(PAID)
4趟大、之后紅包系統(tǒng)會通知微信IM系統(tǒng),發(fā)送消息通知群里的用戶铣焊,此時紅包狀態(tài)為(NOTIFIED)
5.1逊朽、群里的用戶把紅包搶光了,紅包狀態(tài)被系統(tǒng)置為已搶光(RUNOUT)
5.2曲伊、還有一種可能叽讳,如果群里都是程序員,忙著擼代碼坟募,沒時間搶紅包岛蚤,一定時間后紅包自動退款到支付賬號,紅包狀態(tài)便為(REFUND)

這只是一個正常業(yè)務(wù)流程的紅包狀態(tài)機(jī)懈糯,異常情況如拆分失敗涤妒、支付失敗、通知失敗赚哗、退款失敗等情況也同理存在一個狀態(tài)機(jī)器她紫。為了方便業(yè)務(wù)實體狀態(tài)回滾和校正,狀態(tài)機(jī)要盡量設(shè)計精簡屿储,轉(zhuǎn)移到下一個狀態(tài)的邊盡可能的只有一條路徑(終結(jié)狀態(tài)會例外)贿讹,這樣在回滾和校正時能夠明確前一個狀態(tài)和后一個狀態(tài)。舉個例子够掠,如果系統(tǒng)發(fā)現(xiàn)紅包一直處于PAID狀態(tài)民褂,而并沒有流轉(zhuǎn)到NOTIFIED狀態(tài),能夠判斷是通知群用戶出現(xiàn)異常疯潭,可以根據(jù)實際情況重新通知群用戶或者將超期紅包退款赊堪。

4、恢復(fù)日志

恢復(fù)日志是程序現(xiàn)場的記錄袁勺,也是業(yè)務(wù)數(shù)據(jù)恢復(fù)的重要依據(jù)雹食。恢復(fù)日志log要求全局唯一的requestId來標(biāo)示請求(實際的業(yè)務(wù)場景可采用不會重復(fù)有含義的業(yè)務(wù)id)期丰,出現(xiàn)異常群叶,可以根據(jù)requestId維度redo和undo業(yè)務(wù)操作,恢復(fù)日志具體可分為三部分 :

  • requestId請求開始時钝荡,記錄REQUEST START requestId
  • 本地修改時街立,記錄全部的(requestId,x埠通,originalValue, destValue)四元組赎离,x代表操作對象,修改前x的值為originalValue端辱,本次修改的目的操作值為destValue
  • requestId結(jié)束時梁剔,記錄REQUEST End requestId\n恢復(fù)日志是系統(tǒng)從不一致的狀態(tài)恢復(fù)到一致狀態(tài)的重要數(shù)據(jù)虽画,丟失恢復(fù)日志,意味著不一致可能無法恢復(fù)荣病。為什么是可能码撰,因為有時可以通過狀態(tài)機(jī)對不一致的狀態(tài)進(jìn)行恢復(fù)。\n\n5个盆、異步校驗

徹底解決分布式一致性問題脖岛,有著名的Paxos算法,通過該算法分布式系統(tǒng)自發(fā)達(dá)成一致性颊亮。而在具體的業(yè)務(wù)場景柴梆,完全不需要系統(tǒng)自發(fā)的達(dá)成共識,我們只要在業(yè)務(wù)系統(tǒng)外部加上嚴(yán)格的業(yè)務(wù)約束终惑,用來仲裁業(yè)務(wù)系統(tǒng)的狀態(tài)绍在。通過異步校驗,可以發(fā)現(xiàn)分布式系統(tǒng)中的異常狀態(tài)雹有,并通過恢復(fù)日志進(jìn)行腳本批量恢復(fù)或者人工處理恢復(fù)揣苏,根據(jù)校驗的粒度有 :

  • 根據(jù)業(yè)務(wù)實體id校驗,使用消息隊列件舵,將需要校驗業(yè)務(wù)id投遞給校驗系統(tǒng),進(jìn)行異步校驗脯厨。
  • 根據(jù)時間維度批量校驗铅祸,使用異步調(diào)度框架,根據(jù)時間粒度批量獲取進(jìn)行異步校驗合武。
  • 此外临梗,并不是所有系統(tǒng)都有可靠消息隊列和調(diào)度服務(wù)支撐,業(yè)務(wù)系統(tǒng)可以增加一個本地業(yè)務(wù)id校驗回執(zhí)字段稼跳,校驗系統(tǒng)根據(jù)校驗步驟回調(diào)設(shè)置校驗回執(zhí)字段盟庞,并對校驗未通過的數(shù)據(jù)進(jìn)行重校驗或者訂正。

總結(jié)

分布式最終一致性問題汤善,后端程序員在實際開發(fā)中經(jīng)常遇到什猖。在實際系統(tǒng)開發(fā)中為了確保最終一致性,往往需要組合多個技術(shù)點打出組合拳红淡,因為招數(shù)是死的不狮,程序員是活的≡诤担總結(jié)上面提到的技術(shù)點摇零,我們可以通過冪等和重試機(jī)制,使得不一致數(shù)據(jù)能夠自動恢復(fù)桶蝎;通過異步校驗機(jī)制發(fā)現(xiàn)業(yè)務(wù)系統(tǒng)的不一致數(shù)據(jù)驻仅;通過狀態(tài)機(jī)和恢復(fù)日志谅畅,糾正不一致的業(yè)務(wù)數(shù)據(jù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末噪服,一起剝皮案震驚了整個濱河市毡泻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芯咧,老刑警劉巖牙捉,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異敬飒,居然都是意外死亡邪铲,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門无拗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带到,“玉大人,你說我怎么就攤上這事英染±咳牵” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵四康,是天一觀的道長搪搏。 經(jīng)常有香客問我,道長闪金,這世上最難降的妖魔是什么疯溺? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮哎垦,結(jié)果婚禮上囱嫩,老公的妹妹穿的比我還像新娘。我一直安慰自己漏设,他們只是感情好墨闲,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著郑口,像睡著了一般鸳碧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潘酗,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天杆兵,我揣著相機(jī)與錄音,去河邊找鬼仔夺。 笑死琐脏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播日裙,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吹艇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昂拂?” 一聲冷哼從身側(cè)響起受神,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎格侯,沒想到半個月后鼻听,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡联四,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年撑碴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朝墩。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡醉拓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出收苏,到底是詐尸還是另有隱情亿卤,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布鹿霸,位于F島的核電站排吴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏懦鼠。R本人自食惡果不足惜傍念,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望葛闷。 院中可真熱鬧钝吮,春花似錦赚抡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣泊。三九已至,卻和暖如春嘶摊,著一層夾襖步出監(jiān)牢的瞬間延蟹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工叶堆, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留阱飘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像沥匈,于是被迫代替她去往敵國和親蔗喂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354