海量數(shù)據(jù)下如何實(shí)現(xiàn)分布式事務(wù)



點(diǎn)擊上方藍(lán)色“邁莫coding”,選擇“設(shè)為星標(biāo)”
今天和大家聊一聊分布式系統(tǒng)中分布式事務(wù),無論是初級(jí)程序員,還是中高級(jí)程序員,在面試過程中分布式事務(wù)問題都是必備的。但不同階段的要求是不同的。一般被分布式事務(wù)問題,腦海中直接蹦出解決方案:2pc、3pc、MQ等等。但是這幾種方案他的落地可行性考慮過嗎?什么樣的場景下使用什么樣的方案,思考過嗎?
接下來,我會(huì)以一個(gè)案例作為切入點(diǎn)進(jìn)行闡述,來去分析每種方案的可行性。
案列背景
在互聯(lián)網(wǎng)分布式場景中,原先單機(jī)系統(tǒng)會(huì)被拆分成多個(gè)子系統(tǒng),進(jìn)行獨(dú)立部署。這樣就會(huì)導(dǎo)致想要完成一次寫入操作,你就需要協(xié)同多個(gè)子系統(tǒng),這就帶來分布式事務(wù)問題(分布式事務(wù):一次大操作被拆分成多個(gè)小操作,而多個(gè)小操作分布在不同的服務(wù)器上,分布式事務(wù)就需要保證多個(gè)小操作要么同時(shí)成功,要么同時(shí)失敗)。問題來了,怎樣設(shè)計(jì)才能保證數(shù)據(jù)的一致性問題?這也是今天這篇問題所闡述的中心。
案例以商城為例,用戶訂購某個(gè)商品后,會(huì)涉及到訂單系統(tǒng)、庫存系統(tǒng)、優(yōu)惠券系統(tǒng)(為了方便,就拿這三個(gè)系統(tǒng)闡述)。只有這三個(gè)系統(tǒng)執(zhí)行成功后,才能表示該用戶下單訂購成功。否則下單失敗。

可以從圖中看出,用戶購買某個(gè)商品,需要庫存系統(tǒng)、訂單系統(tǒng)、優(yōu)惠券系統(tǒng)這三個(gè)系統(tǒng)的協(xié)調(diào),才能保證用戶下單成功。而由于每個(gè)系統(tǒng)可能會(huì)進(jìn)行獨(dú)立部署,這就導(dǎo)致會(huì)出現(xiàn)分布式事務(wù)問題,如何保證數(shù)據(jù)的一致性,并且在保證數(shù)據(jù)的一致性前提下,如何降低時(shí)延性,也是考察一個(gè)人的技術(shù)功底。
案例分析
這個(gè)案例一看就是典型的分布式事務(wù)問題,解決方案也有很多,比如2PC、3PC、基于MQ等等解決方案。但實(shí)際業(yè)務(wù)場景不太會(huì)用到2PC、3PC,互聯(lián)網(wǎng)基本上是通過MQ來解決分布式事務(wù)問題的。
所以在面試回答上可以先提出解決分布式事務(wù)問題的幾種解決方案,比如2PC、3PC、基于MQ消息隊(duì)列。然后分析每個(gè)方案優(yōu)缺點(diǎn),最終選擇出自己的方案。這樣回答,會(huì)讓面試官認(rèn)為應(yīng)聘者有自己獨(dú)立的思考見解,不僅理論知識(shí)扎實(shí),也會(huì)結(jié)合業(yè)務(wù)場景選出合適的解決方案。這樣非常加分的。
但做到上述回答模式,需要對(duì)理論知識(shí)有更深次的理解,對(duì)每個(gè)解決方案都有自己獨(dú)立的思考,分析出不同解決方案的優(yōu)缺點(diǎn)。所以接下來,我會(huì)和大家一起來分析在分布式事務(wù)場景中三種解決方案。
案例解答
1 基于2pc提出的解決方案
2pc是分布式事務(wù)教父級(jí)協(xié)議,也是數(shù)據(jù)庫領(lǐng)域解決分布式事務(wù)最典型的協(xié)議。它的處理過程分為準(zhǔn)備階段和提交階段,每個(gè)階段由協(xié)調(diào)者(Coordinator)和參與者(Participant)共同完成。
準(zhǔn)備階段:在準(zhǔn)備階段,協(xié)調(diào)者將通知事務(wù)參與者準(zhǔn)備提交事務(wù),然后進(jìn)入表決過程。在表決過程中,參與者將告知協(xié)調(diào)者自己的決策:同意(事務(wù)參與者本地事務(wù)執(zhí)行成功)或取消(本地事務(wù)執(zhí)行故障),在第一階段,參與節(jié)點(diǎn)并沒有進(jìn)行Commit操作。
提交階段:在提交階段,協(xié)調(diào)者將基于第一個(gè)階段的投票結(jié)果進(jìn)行決策:提交或取消這個(gè)事務(wù)。必須當(dāng)且僅當(dāng)所有的參與者同意提交,協(xié)調(diào)者才會(huì)通知各個(gè)參與者提交事務(wù),否則協(xié)調(diào)者將通知各個(gè)參與者取消事務(wù)。
參與者在接收到協(xié)調(diào)者發(fā)來的消息后將執(zhí)行對(duì)應(yīng)的操作,也就是本地 Commit 或者 Rollback。
那么它的執(zhí)行過程到底是怎么樣的?以本案例為基準(zhǔn),假設(shè)訂單數(shù)據(jù),商品數(shù)據(jù)和優(yōu)惠券數(shù)據(jù)分別保存在數(shù)據(jù)庫 D1,數(shù)據(jù)庫 D2 和數(shù)據(jù)庫 D3 上。
準(zhǔn)備階段:協(xié)調(diào)者會(huì)通知參與者(訂單系統(tǒng)、庫存系統(tǒng)、優(yōu)惠券系統(tǒng))準(zhǔn)備提交事務(wù),相當(dāng)于在準(zhǔn)備階段,參與者已經(jīng)執(zhí)行完commit階段之前所有操作,當(dāng)所有的參與者返回YES時(shí),那么協(xié)調(diào)者進(jìn)入提交階段,此時(shí)第一階段結(jié)束。

提交階段:當(dāng)收到所有數(shù)據(jù)庫實(shí)例的 Yes 后,協(xié)調(diào)者會(huì)發(fā)出提交指令。每個(gè)數(shù)據(jù)庫接受指令進(jìn)行本地操作,正式提交更新數(shù)據(jù),然后向協(xié)調(diào)者返回 Ack 消息,事務(wù)結(jié)束。

上述描述的就是2PC實(shí)現(xiàn)的基本原理。但是仔細(xì)想想,又會(huì)發(fā)現(xiàn)2PC會(huì)導(dǎo)致數(shù)據(jù)不一致、死鎖、性能低下等問題。這也是我們?cè)诩夹g(shù)選型中需要考慮的。
2PC缺點(diǎn)
死鎖:在執(zhí)行過程中,一旦協(xié)調(diào)者出現(xiàn)故障,會(huì)導(dǎo)致數(shù)據(jù)庫阻塞,尤其在提交階段,如果發(fā)生故障,數(shù)據(jù)還處于資源鎖定狀態(tài),將無法完成后續(xù)的事務(wù)提交操作。
性能低下:2PC階段中,數(shù)據(jù)庫會(huì)對(duì)資源進(jìn)行鎖定,如果此時(shí)其他事務(wù)相對(duì)被鎖定的數(shù)據(jù)進(jìn)行操作時(shí),發(fā)現(xiàn)數(shù)據(jù)處于鎖定狀態(tài),那么他們只能進(jìn)行阻塞等待,導(dǎo)致出現(xiàn)高延遲和性能低下問題。
數(shù)據(jù)不一致:在提交階段,協(xié)調(diào)者向參與者發(fā)送commit請(qǐng)求,此時(shí)由于網(wǎng)絡(luò)異常,導(dǎo)致部分參與者沒有收到commit請(qǐng)求,無法進(jìn)行提交事務(wù),導(dǎo)致數(shù)據(jù)不一致。
上述就是使用2PC解決分布式事務(wù)的基本原理,及其使用2PC會(huì)導(dǎo)致的問題。
2
基于3pc提出的解決方案
為了解決2PC同步阻塞問題,三階段提交在協(xié)調(diào)者和參與者中都引入超時(shí)機(jī)制,并且把二階段提交中準(zhǔn)備階段劃分為兩步:先詢問再鎖定資源,最后真正提交。
三階段中的 Three Phase 分別為 CanCommit、PreCommit、Commit 階段。

Can-Commi階段:3PC 的 CanCommit 階段其實(shí)和 2PC 的準(zhǔn)備階段很像。協(xié)調(diào)者向參與者發(fā)送 Can-Commit 請(qǐng)求,參與者如果可以提交就返回 Yes 響應(yīng),否則返回 No 響應(yīng)。
Pre-Commit階段:協(xié)調(diào)者根據(jù)參與者的反應(yīng)情況來決定是否可以繼續(xù)事務(wù)的 PreCommit 操作。根據(jù)響應(yīng)情況,有以下兩種可能。
Commit階段:該階段才是事務(wù)真正的提交,根據(jù)響應(yīng)情況,分為兩種可能。
協(xié)調(diào)者收到了所有參與者的ACK響應(yīng),那么協(xié)調(diào)者向參與者發(fā)送Commit操作,所有參與者收到命令后,真正提交事務(wù),執(zhí)行成功后,釋放鎖定資源,返回協(xié)調(diào)者ACK響應(yīng),完成事務(wù)。
假如協(xié)調(diào)者未收到部分參與者的ACK響應(yīng),可能是參與者返回的不是ACK確認(rèn)機(jī)制,也有可能是超時(shí),中斷事務(wù)。
協(xié)調(diào)者收到所有參與者的ACK響應(yīng)后,由于網(wǎng)絡(luò)原因,部分參與者未收到commit命令,那么參與者超市之后也會(huì)執(zhí)行事務(wù)提交操作。
上述就是3PC的實(shí)現(xiàn)原理。在2PC基礎(chǔ)之上進(jìn)行改進(jìn)。
3pc與2pc的改進(jìn)之處
引入超時(shí)機(jī)制:在2PC階段中,只有協(xié)調(diào)者擁有超時(shí)機(jī)制,而在3PC階段中,協(xié)調(diào)者和參與者都引入超時(shí)機(jī)制,保證參與者即使遇到協(xié)調(diào)者宕機(jī)情形下,也可以超時(shí)釋放鎖定資源,反之造成死鎖。
添加預(yù)提交階段:在 2PC 的準(zhǔn)備階段和提交階段之間,插入一個(gè)準(zhǔn)備階段,使 3PC 擁有 CanCommit、PreCommit、Commit 三個(gè)階段,PreCommit 是一個(gè)緩沖,保證了在最后提交階段之前各參與節(jié)點(diǎn)的狀態(tài)是一致的。
3
基于MQ提出的解決方案
基于MQ可靠消息隊(duì)列是目前互聯(lián)網(wǎng)最常用的方式,在應(yīng)對(duì)海量并發(fā)數(shù)據(jù)的場景下,可以起到業(yè)務(wù)解耦和流量削峰的效果。使用MQ解決分布式事務(wù)問題,其核心采用數(shù)據(jù)最終一致性思想。
還是以商城案例為例,用戶下單后,訂單系統(tǒng)調(diào)用優(yōu)惠券系統(tǒng)時(shí),將扣減的優(yōu)惠券發(fā)送到消息隊(duì)列中,優(yōu)惠券系統(tǒng)拉取數(shù)據(jù)進(jìn)行對(duì)應(yīng)業(yè)務(wù)操作,然后保證優(yōu)惠券系統(tǒng)可以正常消費(fèi)成功就可以了,因?yàn)橄㈥?duì)列會(huì)進(jìn)行持久化,即使MQ服務(wù)器發(fā)生宕機(jī),當(dāng)重啟時(shí)消息也不會(huì)丟失。

采用MQ可靠性隊(duì)列不僅保證數(shù)據(jù)的一致性,解決業(yè)務(wù)流程同步流程阻塞問題,還起到了業(yè)務(wù)解耦和流量削峰的效果,這也是互聯(lián)網(wǎng)最常用的方式。目前市場上技術(shù)選型很多,比如RabbitMQ、RocketMQ等等。
但使用MQ可靠性隊(duì)列作為解決分布式事務(wù)的方案,那么光知道這些還不夠,你需要把MQ如何保證數(shù)據(jù)不丟失,MQ的提交機(jī)制等等了解清楚后,才能夠合理運(yùn)用。遇到意外發(fā)生,也能在萌發(fā)階段扼殺掉,不至于背鍋。????。
MQ自動(dòng)應(yīng)答機(jī)制導(dǎo)致數(shù)據(jù)丟失
MQ消息中間價(jià)默認(rèn)情況下采用自動(dòng)提交機(jī)制,也就是說優(yōu)惠券系統(tǒng)消費(fèi)數(shù)據(jù)后,消息中間件會(huì)刪除這個(gè)持久化的消息。
如果采用自動(dòng)提交機(jī)制,就會(huì)出現(xiàn)數(shù)據(jù)丟失的可能性。比如優(yōu)惠券系統(tǒng)消費(fèi)時(shí),由于不可控原因?qū)е缕湎M(fèi)失敗,那這個(gè)時(shí)候消息中間價(jià)中就沒有這條數(shù)據(jù)了,導(dǎo)致數(shù)據(jù)丟失。如果采用手動(dòng)提交機(jī)制,優(yōu)惠券系統(tǒng)消費(fèi)成功后,才會(huì)返回ACK確認(rèn)機(jī)制,可以保證數(shù)據(jù)一定消費(fèi)成功,消息中間件才會(huì)刪除這條持久化數(shù)據(jù)。
MQ數(shù)據(jù)擠壓導(dǎo)致數(shù)據(jù)丟失
分布式系統(tǒng)是基于網(wǎng)絡(luò)進(jìn)行通信,而在網(wǎng)絡(luò)通信的過程中,上下游可能因?yàn)楦鞣N原因而導(dǎo)致消息丟失。比如優(yōu)惠券系統(tǒng)由于流量過大而觸發(fā)限流,不能保證事件消息能夠被及時(shí)地消費(fèi),這個(gè)消息就會(huì)被消息隊(duì)列不斷地重試,最后可能由于超過了最大重試次數(shù)而被丟棄到死信隊(duì)列中。
但實(shí)際上,你需要人工干預(yù)處理移入死信隊(duì)列的消息,于是在這種場景下,事件消息大概率會(huì)被丟棄。出現(xiàn)這個(gè)原因主要是訂單系統(tǒng)無法感知優(yōu)惠券系統(tǒng)的執(zhí)行狀況,優(yōu)惠券系統(tǒng)無法通知訂單系統(tǒng)導(dǎo)致的。
那么基于這條思路,我們這樣干:在訂單系統(tǒng)中維護(hù)一個(gè)消息狀態(tài)表,每條數(shù)據(jù)發(fā)送成功到消息中間件后,在消息狀態(tài)表中更新數(shù)據(jù)未已發(fā)送狀態(tài);當(dāng)優(yōu)惠券系統(tǒng)消費(fèi)完成后,更新狀態(tài)表未已完成狀態(tài);最后定時(shí)任務(wù)不斷輪訓(xùn)消息狀態(tài)表,獲取未完成狀態(tài)的消息,將其再次投遞到消息中間件中進(jìn)行消費(fèi)。這樣就可以保證數(shù)據(jù)一定不丟失。

基于這樣的設(shè)計(jì)方案,會(huì)有一個(gè)問題,會(huì)導(dǎo)致消息重復(fù)問題。比如此時(shí)優(yōu)惠券系統(tǒng)執(zhí)行成功前一秒,定時(shí)任務(wù)掃描到該數(shù)據(jù)處于未完成狀態(tài),將其再次投遞到消息中間件中,而此時(shí)優(yōu)惠券系統(tǒng)更新消息狀態(tài)表。那么這種情況會(huì)導(dǎo)致數(shù)據(jù)消費(fèi),所以需要優(yōu)惠券這邊做處理。
接下來我們就來談?wù)剝?yōu)惠券系統(tǒng)如何保證數(shù)據(jù)不重復(fù)消費(fèi)問題。
MQ保證數(shù)據(jù)冪等性
上述通過消息狀態(tài)表解決消息丟失問題,但隨之引來的是消息重復(fù)消費(fèi)問題。為此,優(yōu)惠券系統(tǒng)作為消費(fèi)者,在消費(fèi)時(shí)需要考慮冪等性,在消息處理之前首先查看數(shù)據(jù)是否已消費(fèi),若未消費(fèi)才進(jìn)行消費(fèi),否則不消費(fèi)。
總結(jié)
總結(jié)
最后對(duì)這篇文章進(jìn)行總結(jié)。今天講述了解決分布式事務(wù)的幾種方案,以及各個(gè)方式的優(yōu)缺點(diǎn)。
基于 MQ 的可靠消息投遞的考核點(diǎn)是可落地性,所以你在回答時(shí)要抓住“雙向確認(rèn)”的核心原則,只要能實(shí)現(xiàn)生產(chǎn)端和消費(fèi)端的雙向確認(rèn),這個(gè)方案就是可落地了,又因?yàn)榛?MQ 來實(shí)現(xiàn),所以天生具有業(yè)務(wù)解耦合流量削峰的優(yōu)勢(shì)。
基于 2PC/3PC 的實(shí)現(xiàn)方案很少有實(shí)際的場景,但你還是要掌握它的實(shí)現(xiàn)原理和存在的問題,因?yàn)槊嬖嚥煌趯?shí)際工作,有些問題的回答是為了告訴面試官:我有這個(gè)能力。盡管它在實(shí)際工作中并不適用。
最后,有一點(diǎn)需要你注意,在實(shí)際工作中,并不是所有的業(yè)務(wù)對(duì)事務(wù)一致性的要求都那么高。因?yàn)楦叩囊笠馕吨嗟某杀?,這也是很多架構(gòu)復(fù)雜度來源之一,所以你要盡可能地站在業(yè)務(wù)實(shí)際場景的立足點(diǎn)來回答分布式事務(wù)問題。
參考資料
1.https://kaiwu.lagou.com/course/courseInfo.htm?courseId=592#/detail/pc?id=6054
2.https://kaiwu.lagou.com/course/courseInfo.htm?courseId=69#/detail/pc?id=1906
分割線
往期推薦
文章也會(huì)持續(xù)更新,可以微信搜索「 IT界學(xué)習(xí)筆記 」第一時(shí)間閱讀。每天分享優(yōu)質(zhì)文章、大廠經(jīng)驗(yàn)、大廠面經(jīng),助力面試,是每個(gè)程序員值得關(guān)注的平臺(tái)。

你點(diǎn)的每個(gè)贊,我都認(rèn)真當(dāng)成了喜歡
