文章摘要:原來大型分布式/微服務(wù)系統(tǒng)中解決數(shù)據(jù)一致性問題纽疟,居然是通過……
目前云計算盛泡、大數(shù)據(jù)捺癞、互聯(lián)網(wǎng)領(lǐng)域的大部分系統(tǒng)都采用了SOA夷蚊、微服務(wù)化的架構(gòu)。一個涉及端到端全鏈路的業(yè)務(wù)操作往往會由多個服務(wù)和數(shù)據(jù)庫實(shí)例共同完成髓介。因此惕鼓,在一致性要求較高的業(yè)務(wù)場景中,如何保證多個服務(wù)之間RPC調(diào)用后的數(shù)據(jù)一致將成為關(guān)鍵點(diǎn)唐础。
一箱歧、分布式系統(tǒng)/SOA/微服務(wù)架構(gòu)的特點(diǎn):
在大型分布式系統(tǒng)中要同時能夠滿足,分布式一致性(Consistency)一膨、可用性(Availability)和分區(qū)容忍性(Partitiontolerance)呀邢,是不存在的。在大多數(shù)情況下只能滿足其中的2項(xiàng)豹绪,而實(shí)現(xiàn)系統(tǒng)的最終一致性(Base理論)价淌。
(1)CAP特點(diǎn):
a.一致性(Consistency):(同樣數(shù)據(jù)在分布式系統(tǒng)的各個節(jié)點(diǎn)上都是一致的)
b.可用性(Availability):(所有在分布式系統(tǒng)活躍的節(jié)點(diǎn)都能夠處理操作且能響應(yīng)查詢)
c.分區(qū)容忍性(Partition
Tolerance) :(如果出現(xiàn)了網(wǎng)絡(luò)故障、一部分節(jié)點(diǎn)無法通信瞒津,但是系統(tǒng)仍能夠工作)
(2)ACID特點(diǎn):
a.原子性(Atomicity)
一個事務(wù)(transaction)中的所有操作蝉衣,要么全部完成,要么全部不完成巷蚪,不會結(jié)束在中間某個環(huán)節(jié)病毡。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài)屁柏,就像這個事務(wù)從來沒有執(zhí)行過一樣剪验。
b.一致性(Consistency)
事務(wù)的一致性指的是在一個事務(wù)執(zhí)行之前和執(zhí)行之后數(shù)據(jù)庫都必須處于一致性狀態(tài)。如果事務(wù)成功地完成前联,那么系統(tǒng)中所有變化將正確地應(yīng)用,系統(tǒng)處于有效狀態(tài)娶眷。如果在事務(wù)中出現(xiàn)錯誤似嗤,那么系統(tǒng)中的所有變化將自動地回滾,系統(tǒng)返回到原始狀態(tài)届宠。
c.隔離性(Isolation)
指的是在并發(fā)環(huán)境中烁落,當(dāng)不同的事務(wù)同時操縱相同的數(shù)據(jù)時,每個事務(wù)都有各自的完整數(shù)據(jù)空間豌注。由并發(fā)事務(wù)所做的修改必須與任何其他并發(fā)事務(wù)所做的修改隔離伤塌。事務(wù)查看數(shù)據(jù)更新時,數(shù)據(jù)所處的狀態(tài)要么是另一事務(wù)修改它之前的狀態(tài)轧铁,要么是另一事務(wù)修改它之后的狀態(tài)每聪,事務(wù)不會查看到中間狀態(tài)的數(shù)據(jù)。
d.持久性(Durability)
指的是只要事務(wù)成功結(jié)束,它對數(shù)據(jù)庫所做的更新就必須永久保存下來药薯。即使發(fā)生系統(tǒng)崩潰绑洛,重新啟動數(shù)據(jù)庫系統(tǒng)后,數(shù)據(jù)庫還能恢復(fù)到事務(wù)成功結(jié)束時的狀態(tài)童本。
二真屯、分布式事務(wù)的基本介紹
分布式事務(wù)服務(wù)(Distributed
Transaction Service,DTS)是一種分布式事務(wù)框架穷娱,用來確保在大規(guī)模分布式/微服務(wù)環(huán)境下端到端業(yè)務(wù)操作的最終一致性绑蔫。
由CAP定理可知,任何大型的分布式系統(tǒng)/微服務(wù)在一致性泵额、可用性和分區(qū)容忍這三點(diǎn)上只能保證其中的兩點(diǎn)配深。由于在分布式系統(tǒng)中經(jīng)常發(fā)生丟包、網(wǎng)絡(luò)故障梯刚,分區(qū)容忍性是必須要滿足的凉馆,同時為了兼顧高可用性,絕大部分系統(tǒng)都將強(qiáng)一致性需求轉(zhuǎn)化成最終一致性的需求亡资,并通過冪等機(jī)制保證了數(shù)據(jù)的最終一致性澜共。
三、常用的分布式技術(shù)介紹
(1)本地消息表(經(jīng)典的ebay模式)
該方案的核心思想在于分布式系統(tǒng)在處理任務(wù)時通過消息日志的方式來異步執(zhí)行锥腻。消息日志可以存儲至本地文本嗦董、數(shù)據(jù)庫或消息隊列,然后再通過業(yè)務(wù)規(guī)則定時任務(wù)或人工自動重試瘦黑。以在線支付系統(tǒng)的跨行轉(zhuǎn)賬為例:
第一步京革,偽代碼如下,對用戶id為A的賬戶扣款1000元幸斥,通過本地事務(wù)將事務(wù)消息(包括本地事務(wù)id匹摇、支付賬戶、收款賬戶甲葬、金額廊勃、狀態(tài)等)插入至消息表:
Begintransaction
update user_account set amount = amount - 1000where userId = 'A'
insert intotrans_message(xid,payAccount,recAccount,amount,status)values(uuid(),'A','B',1000,1);
endtransaction
commit;
第二步,通知對方用戶id為B经窖,增加1000元坡垫,通常通過消息MQ的方式發(fā)送異步消息,對方訂閱并監(jiān)聽消息后自動觸發(fā)轉(zhuǎn)賬的操作画侣;這里為了保證冪等性冰悠,防止觸發(fā)重復(fù)的轉(zhuǎn)賬操作,需要在執(zhí)行轉(zhuǎn)賬操作方新增一個trans_recv_log表用來做冪等配乱,在第二階段收到消息后溉卓,通過判斷trans_recv_log表來檢測相關(guān)記錄是否被執(zhí)行皮迟,如果未被執(zhí)行則會對B賬戶余額執(zhí)行加1000元的操作,并會將該記錄增加至trans_recv_log,事件結(jié)束后通過回調(diào)更新trans_message的狀態(tài)值的诵。
(2)消息中間件
a.非事務(wù)消息中間件
這里仍然以上面跨行轉(zhuǎn)賬為例万栅,我們很難保證在扣款完成之后對MQ投遞消息的操作就一定能成功。這樣一致性似乎很難保證西疤。以下偽代碼說明了消息投遞的異常:
try{
boolean result = dao.update(model);//更新數(shù)據(jù)庫失敗拋出異常
if(result){
mq.send(model);//如果MQ超時或者接收方處理失敗,拋出異常
}
}catch(Exception ex){
rollback();//如果異撤沉#回滾
}
對于以上的運(yùn)行情況主要有以下幾種:
1.操作數(shù)據(jù)庫成功,向MQ中投遞消息也成功代赁,該屬于正常情況职恳,一切都OK夫植。
2.操作數(shù)據(jù)庫失敗,不會向MQ中投遞消息了。
3.操作數(shù)據(jù)庫成功陆蟆,但是向MQ中投遞消息時失敗浩嫌,向外拋出了異常钠糊,剛剛執(zhí)行的更新數(shù)據(jù)庫的操作將被回滾垫言。
從上面分析的幾種情況來看,基本上能確保,發(fā)送消息的可靠性瞻讽。我們再來分析下消費(fèi)者端的問題:
1.接收者取出消息后鸳吸,消費(fèi)者對應(yīng)的業(yè)務(wù)操作要執(zhí)行成功。如果業(yè)務(wù)執(zhí)行失敗速勇,消息不能失效或者丟失晌砾。需要保證消息與業(yè)務(wù)操作一致。
2.盡量確保消息的冪等性烦磁。如果出現(xiàn)重復(fù)消息投遞养匈,能夠進(jìn)行冪等而不對業(yè)務(wù)產(chǎn)生影響。
b.支持事務(wù)的消息中間件
Apache開源的RocketMQ中間件能夠支持一種事務(wù)消息機(jī)制都伪,確保本地操作和發(fā)送消息的異步處理達(dá)到本地事務(wù)的結(jié)果一致呕乎。
第一階段,RocketMQ在執(zhí)行本地事務(wù)之前陨晶,會先發(fā)送一個Prepared消息楣嘁,并且會持有這個消息的接口回查地址。
第二階段珍逸,執(zhí)行本地事物操作。
第三階段聋溜,確認(rèn)消息發(fā)送谆膳,通過第一階段拿到的接口地址URL執(zhí)行回查,并修改狀態(tài)撮躁,如果本地事務(wù)成功漱病,則修改狀態(tài)為已提交,否則修改狀態(tài)為已回滾。
其中杨帽,如果第三階段的確認(rèn)消息發(fā)送失敗后漓穿,RocketMQ會有定時任務(wù)掃描集群中的事務(wù)消息,如果發(fā)現(xiàn)還是處于prepare狀態(tài)的消息注盈,它會向消息發(fā)送者確認(rèn)本地事務(wù)是否已執(zhí)行成功晃危。RocketMQ會根據(jù)發(fā)送端設(shè)置的策略來決定是回滾還是繼續(xù)發(fā)送確認(rèn)消息。這樣就保證了消息的發(fā)送與本地事務(wù)同時成功或同時失敗老客。
再回到上面轉(zhuǎn)賬的例子僚饭,如果用戶A的賬戶余額已經(jīng)減少,且消息已經(jīng)發(fā)送成功胧砰,作為消費(fèi)者用戶B開始消費(fèi)這條消息鳍鸵,這個時候就會出現(xiàn)消費(fèi)失敗和消費(fèi)超時兩個問題,解決超時問題的思路就是一直重試尉间,直到消費(fèi)端消費(fèi)消息成功偿乖,整個過程中有可能會出現(xiàn)消息重復(fù)的問題,就需要采用前面說的冪等方案來進(jìn)行處理哲嘲。
分布式事務(wù)—2PC協(xié)議
為了解決大型分布式/微服務(wù)系統(tǒng)中的一致性問題贪薪,業(yè)界比較流行的做法是采用比較著名的有二階提交協(xié)議(2
Phase Commitment Protocol)和三階提交協(xié)議(3
PhaseCommitment Protocol)〕肺茫考慮到性能問題古掏,三階段提交協(xié)議目前較少被采用。本文也主要介紹二階段協(xié)議侦啸。
2PC協(xié)議
二階段提交協(xié)議是分布式系統(tǒng)中較為經(jīng)典的處理數(shù)據(jù)一致性的解決方案槽唾。在大型的集群環(huán)境中,對于單體微服務(wù)本身而言雖然能夠通過代碼質(zhì)量光涂、Mock測試等方法來確保自身服務(wù)的可用性庞萍,但是無法能夠保證其他服務(wù)的可用性。當(dāng)一個全鏈路的端到端業(yè)務(wù)操作忘闻,常常會跨多個節(jié)點(diǎn)钝计、多個應(yīng)用,為了能夠保證全局事務(wù)的ACID特性齐佳,需要引入一個協(xié)調(diào)組件(這里稱之為TM)來控制所有服務(wù)參與者(這里稱之為RM)的操作結(jié)果私恬,根據(jù)所有參與者的反饋結(jié)果來決定整個分布式事務(wù)究竟是提交還是回滾的結(jié)果。
第一階段:稱為準(zhǔn)備(prepare)階段炼吴。事務(wù)協(xié)調(diào)者向各個服務(wù)應(yīng)用發(fā)送prepare請求本鸣,服務(wù)應(yīng)用在得到請求后做預(yù)處理操作,預(yù)處理可能是做預(yù)檢查硅蹦,也可能是把請求臨時存儲荣德,可以理解為是一種試探性地提交闷煤。下面是一般的步驟:
a.事務(wù)協(xié)調(diào)者會問所有的參與者服務(wù),是否可以提交操作涮瞻。
b.各個參與者開始事務(wù)執(zhí)行的準(zhǔn)備工作:如資源上鎖鲤拿,預(yù)留資源,寫回滾/重試的log署咽。
c.參與者響應(yīng)協(xié)調(diào)者近顷,如果事務(wù)準(zhǔn)備工作成功,則回應(yīng)“可以提交”艇抠,否則回應(yīng)拒絕提交幕庐。
第二階段:稱為提交(commit)/回滾(rollback)階段。是指事務(wù)真正提交或者回滾的階段家淤。如果事務(wù)協(xié)調(diào)者發(fā)現(xiàn)事務(wù)參與者有一個在prepare階段出現(xiàn)失敗异剥,則會要求所有的參與者進(jìn)行回滾。如果協(xié)調(diào)者發(fā)現(xiàn)所有的參與者都prepare操作都是成功絮重,那么他將向所有的參與者發(fā)出提交請求冤寿,這時所有參與者才會正式提交。由此保證了要求全部提交成功青伤,要么全部失敗督怜。下面是具體步驟:
a.如果所有的參與者都回應(yīng)“可以提交”,那么協(xié)調(diào)者向所有參與者發(fā)送“正式提交”的命令狠角。參與者完成正式提交号杠,并釋放所有資源,然后回應(yīng)“完成”丰歌,協(xié)調(diào)者收集各個服務(wù)的“完成”回應(yīng)后結(jié)束事務(wù)姨蟋。
b.如果有一個參與者回應(yīng)“拒絕提交”,那么協(xié)調(diào)者向所有的參與者發(fā)送“回滾操作”立帖,并釋放所有的資源眼溶,然后回應(yīng)“回滾完成”,協(xié)調(diào)者收集各個服務(wù)應(yīng)用的“回滾”返回后晓勇,取消整體的分布式事務(wù)堂飞。
下圖為二階段的成功和失敗示例圖:
二階段提交協(xié)議解決的是分布式系統(tǒng)/微服務(wù)架構(gòu)中數(shù)據(jù)強(qiáng)一致性的問題,其原理簡單绑咱,但缺點(diǎn)也是存在绰筛,主要缺點(diǎn)如下:
a.單點(diǎn)問題:協(xié)調(diào)者在整個二階段中的作用非常重要,一旦部署協(xié)調(diào)者組件服務(wù)的節(jié)點(diǎn)出現(xiàn)不可用宕機(jī)情況描融,那么會影響整個分布式系統(tǒng)的正常運(yùn)行别智。
b.同步阻塞:二階段提交執(zhí)行過程中,所有服務(wù)參與者需要服從協(xié)調(diào)者的統(tǒng)一調(diào)度稼稿,期間處于阻塞狀態(tài)薄榛,會一定程度上影響整個系統(tǒng)的效率。