前言
事務是包含一系列操作的一個有邊界的工作序列,有明確的開始和結(jié)束標志烂翰,并且要具備原子性夯缺,要么被完全執(zhí)行蚤氏,要么完全失敗甘耿。因此系統(tǒng)做了微服務拆分以后,面臨的最大的問題就是如何保證事務的一致性竿滨,原先的事務在一個進程中只要保證單進程中的事務性就可以佳恬,服務拆分做了分布式以后,就面臨著一個事務涉及到多應用節(jié)點之間如何保證事務的ACID于游。分布式事務毁葱,就是在分布式系統(tǒng)中運行的事務,由多個本地事務組合而成贰剥。
分布式事務主要是解決在分布式環(huán)境下倾剿,組合事務的一致性問題。實現(xiàn)分布式事務有以下 3 種基本方法:
基于 XA 協(xié)議的二階段提交協(xié)議方法;
三階段提交協(xié)議方法前痘;
基于消息的最終一致性方法凛捏。
其中,基于 XA 協(xié)議的二階段提交協(xié)議方法和三階段提交協(xié)議方法芹缔,采用了強一致性,遵從 ACID,基于消息的最終一致性方法钱床,采用了最終一致性积担,遵從 BASE 理論。
XA二階段提交
XA 是一個分布式事務協(xié)議芝硬,規(guī)定了事務管理器和資源管理器接口蚜点。因此,XA 協(xié)議可以分為兩部分拌阴,即事務管理器和本地資源管理器禽额。為了保證它們的一致性,我們需要引入一個協(xié)調(diào)者來管理所有的節(jié)點皮官,并確保這些節(jié)點正確提交操作結(jié)果脯倒,若提交失敗則放棄事務。
兩階段提交協(xié)議的執(zhí)行過程捺氢,分為投票(voting)和提交(commit)兩個階段:
1)投票為第一階段藻丢,協(xié)調(diào)者(Coordinator,即事務管理器)會向事務的參與者(Cohort摄乒,即本地資源管理器)發(fā)起執(zhí)行操作的 CanCommit 請求悠反,并等待參與者的響應。參與者接收到請求后馍佑,會執(zhí)行請求中的事務操作斋否,記錄日志信息但不提交,待參與者執(zhí)行成功拭荤,則向協(xié)調(diào)者發(fā)送“Yes”消息茵臭,表示同意操作;若不成功舅世,則發(fā)送“No”消息旦委,表示終止操作。
2)當所有的參與者都返回了操作結(jié)果(Yes 或 No 消息)后雏亚,系統(tǒng)進入了提交階段缨硝。在提交階段,協(xié)調(diào)者會根據(jù)所有參與者返回的信息向參與者發(fā)送 DoCommit 或 DoAbort 指令:
若協(xié)調(diào)者收到的都是“Yes”消息罢低,則向參與者發(fā)送“DoCommit”消息查辩,參與者會完成剩余的操作并釋放資源,然后向協(xié)調(diào)者返回“HaveCommitted”消息;
如果協(xié)調(diào)者收到的消息中包含“No”消息宜岛,則向所有參與者發(fā)送“DoAbort”消息匀钧,此時發(fā)送“Yes”的參與者則會根據(jù)之前執(zhí)行操作時的回滾日志對操作進行回滾,然后所有參與者會向協(xié)調(diào)者發(fā)送“HaveCommitted”消息谬返;
協(xié)調(diào)者接收到“HaveCommitted”消息之斯,就意味著整個事務結(jié)束了。
下面舉個兩階段提交的例子:
1)第一階段:訂單系統(tǒng)中將與用戶 A 有關的訂單數(shù)據(jù)庫鎖住遣铝,準備好增加一條關于用戶 A 購
買 10套口罩的信息佑刷,并將同意消息“Yes”回復給協(xié)調(diào)者。而庫存系統(tǒng)由于 口罩庫存不
足酿炸,出貨失敗瘫絮,因此向協(xié)調(diào)者回復了一個終止消息“No”。
2)第二階段:由于庫存系統(tǒng)操作不成功填硕,因此麦萤,協(xié)調(diào)者就會向訂單系統(tǒng)和庫存系統(tǒng)發(fā)送“DoAbort”消息。訂單系統(tǒng)接收到“DoAbort”消息后扁眯,將系統(tǒng)內(nèi)的數(shù)據(jù)退回到?jīng)]有用戶 A 購買 10套口罩的版本壮莹,并釋放鎖住的數(shù)據(jù)庫資源。訂單系統(tǒng)和庫存系統(tǒng)完成操作
后姻檀,向協(xié)調(diào)者發(fā)送“HaveCommitted”消息命满,表示完成了事務的撤銷操作。
兩階段提交的問題主要有下面幾種:
- 同步阻塞問題:二階段提交算法在執(zhí)行過程中绣版,所有參與節(jié)點都是事務阻塞型的胶台。也就是說,當本地資源管理器占有臨界資源時杂抽,其他資源管理器如果要訪問同一臨界資源诈唬,會處于阻塞狀態(tài)。
- 單點故障問題:基于 XA 的二階段提交算法類似于集中式算法缩麸,一旦事務管理器發(fā)生故障铸磅,整個系統(tǒng)都處于停滯狀態(tài)。尤其是在提交階段匙睹,一旦事務管理器發(fā)生故障愚屁,資源管理器會由于等待管理器的消息,而一直鎖定事務資源痕檬,導致整個系統(tǒng)被阻塞。
- 數(shù)據(jù)不一致問題:在提交階段送浊,當協(xié)調(diào)者向參與者發(fā)送 DoCommit 請求之后梦谜,如果發(fā)生了局部網(wǎng)絡異常,或者在發(fā)送提交請求的過程中協(xié)調(diào)者發(fā)生了故障,就會導致只有一部分參與者接收到了提交請求并執(zhí)行提交操作唁桩,但其他未接到提交請求的那部分參與者則無法執(zhí)行事務提交闭树。于是整個分布式系統(tǒng)便出現(xiàn)了數(shù)據(jù)不一致的問題。
三階段提交
三階段提交協(xié)議(Three-phase commit protocol荒澡,3PC)报辱,是對二階段提交(2PC)的改進。為了解決兩階段提交的同步阻塞和數(shù)據(jù)不一致問題单山,三階段提交引入了超時機制和準備階段碍现。同時在協(xié)調(diào)者和參與者中引入超時機制。如果協(xié)調(diào)者或參與者在規(guī)定的時間內(nèi)沒有接收到來自其他節(jié)點的響應米奸,就會根據(jù)當前的狀態(tài)選擇提交或者終止整個事務昼接。
在第一階段和第二階段中間引入了一個準備階段,也就是在提交階段之前悴晰,加入了一個預提交階段慢睡。在預提交階段排除一些不一致的情況,保證在最后提交之前各參與節(jié)點的狀態(tài)是一致的铡溪。3PC 把 2PC 的提交階段一分為二漂辐,這樣三階段提交協(xié)議就有 CanCommit、PreCommit棕硫、DoCommit 三個階段者吁。
三階段提交:
1)CanCommit階段:協(xié)調(diào)者向參與者發(fā)送請求操作(CanCommit請求),詢問參與者是否可以執(zhí)行事務提交操作饲帅,然后等待參與者的響應复凳;參與者收到CanCommit 請求之后,回復 Yes灶泵,表示可以順利執(zhí)行事務育八;否則回復 No。
2)PreCommit階段:協(xié)調(diào)者根據(jù)參與者的回復情況赦邻,來決定是否可以進行 PreCommit 操作髓棋,如果所有參與者都回復Yes,則所有參與者執(zhí)行PreCommit惶洲,并將Undo 和 Redo信息記錄到事務日志中按声,返回協(xié)調(diào)者ACK。假如任何一個參與者向協(xié)調(diào)者發(fā)送了“No”消息恬吕,或者等待超時之后签则,協(xié)調(diào)者都沒有收到參與者的響應,就執(zhí)行中斷事務的操作铐料。
3)DoCommit階段:DoCmmit 階段進行真正的事務提交渐裂,根據(jù) PreCommit 階段協(xié)調(diào)者發(fā)送的消息豺旬,進入執(zhí)行提交階段或事務中斷階段。a.發(fā)送提交請求柒凉。協(xié)調(diào)者接收到所有參與者發(fā)送的 Ack 響應族阅,從預提交狀態(tài)進入到提交狀態(tài),并向所有參與者發(fā)送 DoCommit 消息膝捞。b.發(fā)送中斷請求坦刀。協(xié)調(diào)者向所有參與者發(fā)送 Abort 請求。事務回滾蔬咬。參與者接收到 Abort 消息之后鲤遥,利用其在 PreCommit 階段記錄的 Undo信息執(zhí)行事務的回滾操作,并釋放所有鎖住的資源计盒。
下面舉個三階段提交的例子:
第一階段:協(xié)調(diào)者發(fā)送給訂單系統(tǒng)canConfirm渴频?,訂單系統(tǒng)檢查資源可用北启。返回消息“Yes”回復給協(xié)調(diào)者卜朗。同時發(fā)送給庫存系統(tǒng)canConfirm?咕村,庫存系統(tǒng)檢查資源是否可用场钉,如果可用返回“Yes”。
第二階段:檢查階段訂單系統(tǒng)和庫存系統(tǒng)都返回“Yes”懈涛,則同時發(fā)給訂單系統(tǒng)和庫存系統(tǒng)PreCommit請求逛万,訂單系統(tǒng)和庫存系統(tǒng)對資源進行鎖庫操作,將操作寫到Redo和Undo日志.最后返回ACK批钠。
第三階段:如果收到訂單系統(tǒng)和庫存系統(tǒng)的PreConfirm階段的ACK后會最后向兩個系統(tǒng)發(fā)送DoCommit指令宇植,訂單系統(tǒng)和庫存系統(tǒng)進行實際操作,成功后返回ACK并釋放資源鎖埋心。
在默認情況下指郁,參與者會自動將超時的事務進行提交,不會像兩階段提交那樣被阻塞
住拷呆。
基于消息的最終一致性
2PC 和 3PC 這兩種方法闲坎,有兩個共同的缺點,一是都需要鎖定資源茬斧,降低系統(tǒng)性能腰懂;二是,沒有解決數(shù)據(jù)不一致的問題项秉⌒辶铮基于分布式消息的最終一致性方案的事務處理,引入了一個消息中間件(MessageQueue伙狐,MQ)涮毫,用于在多個應用之間進行消息傳遞瞬欧。將需要分布式處理的事務通過消息或者日志的方式異步執(zhí)行贷屎,消息或日志可以存到本地文件罢防、數(shù)據(jù)庫或消息隊列中,再通過業(yè)務規(guī)則進行失敗重試唉侄。
下面舉個例子:
- 訂單系統(tǒng)把訂單消息發(fā)給消息中間件咒吐,消息狀態(tài)標記為“待確認”。
- 消息中間件收到消息后属划,進行消息持久化操作恬叹,即在消息存儲系統(tǒng)中新增一條狀態(tài)為“待發(fā)送”的消息。
- 消息中間件返回消息持久化結(jié)果(成功 / 失斖小)绽昼,訂單系統(tǒng)根據(jù)返回結(jié)果判斷如何進行
業(yè)務操作。失敗须蜗,放棄訂單硅确,結(jié)束(必要時向上層返回失敗結(jié)果);成功明肮,則創(chuàng)建訂單菱农。 - 訂單操作完成后,把操作結(jié)果(成功 / 失斒凉馈)發(fā)送給消息中間件循未。
- 消息中間件收到業(yè)務操作結(jié)果后,根據(jù)結(jié)果進行處理:失敗秫舌,刪除消息存儲中的消息的妖,結(jié)束;成功足陨,則更新消息存儲中的消息狀態(tài)為“待發(fā)送(可發(fā)送)”嫂粟,并執(zhí)行消息投遞。
- 如果消息狀態(tài)為“可發(fā)送”钠右,則 MQ 會將消息發(fā)送給支付系統(tǒng)赋元,表示已經(jīng)創(chuàng)建好訂單,需要對訂單進行支付飒房。支付系統(tǒng)也按照上述方式進行訂單支付操作搁凸。
- 訂單系統(tǒng)支付完成后,會將支付消息返回給消息中間件狠毯,中間件將消息傳送給訂單系統(tǒng)护糖。訂單系統(tǒng)再調(diào)用庫存系統(tǒng),進行出貨操作嚼松。
總結(jié)
XA二階段提交協(xié)議 | 三階段提交協(xié)議 | 基于分布式消息的最終一致性方案 | |
---|---|---|---|
算法一致性類別 | 強一致性 | 強一致性 | 最終一致性 |
執(zhí)行方式 | 同步執(zhí)行 | 同步執(zhí)行 | 異步執(zhí)行 |
同步阻塞問題 | 有 | 無 | 無 |
單點故障問題 | 有 | 無 | 無 |
系統(tǒng)并發(fā)度 | XA二階段提交<三階段提交<分布式消息最終一致性 | ||
分布式事務性能 | XA二階段提交<三階段提交<分布式消息最終一致性 |