漫谈分布式事务的那些解决方案
??點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達 今日推薦:牛人 20000 字的 Spring Cloud 總結,太硬核了~作者:平頭哥的技術博文 鏈接:https://juejin.im/post/5e96d8226fb9a03c4c5bcf90事務我們都不陌生,我們常說的事務一般都是指單機事務,即本地事務。那分布式事務是什么?分布式事務就是由多個本地事務組合而成的事務,一般在分布式場景下才會出現。
比如電商平臺中,我們在購物的時候,下單支付這個過程看上去是一氣呵成的,但是背后可能是多個系統的分工合作。訂單系統、支付系統、物流系統等。這些系統部署在不同的服務器上,執行的都是各種的事務,對于電商平臺來說,這就是分布式事務。
本地事務都好解決,有一套現成的事務機制,分布式事務比本地事務就要復雜多。如何實現分布式事務呢?大概有 3 種解決方式:
基于 XA 協議的二階段提交協議。
三階段提交協議。
基于 MQ 的最終一致性。
基于 XA 協議的二階段提交
XA協議由 Tuxedo 首先提出的,并交給X/Open組織,作為資源管理器(數據庫)與事務管理器的接口標準。目前,Oracle、Informix、DB2和Sybase等各大數據庫廠家都提供對XA的支持。----來源百度百科
二階段提交也叫 2PC ,The two-phase commit protocol。首先在二階段提交中有兩個角色:
參與者:本地資源管理器,即事務的執行者,也就是各個業務系統。
協調者:分布式事務的大腦,負責指揮協調各個業務系統提交/回滾事務。
所謂得兩階段提交就是指投票(voting)和提交(commit) 兩個階段,跟選舉制度一樣,先投票,再決定。
投票階段,協調者向參與者發起執行事務操作的請求(CanCommit 請求),并等待參與者響應。
參與者接受到請求后,執行事務請求操作,記錄日志信息但不提交,記錄成功后,向協調者發生 “Yes” 消息,表示同意提交操作,若不成功,則發送“No” 消息,表示不同意這次操作。注意這個過程會鎖定數據。
投票階段得流程圖,大概就是下面這樣子:
二階段提交投票階段提交階段,協調者接受到所有參與者的響應之后,根據返回來的信息情況,向參與者發送提交或回滾請求。
若收到的響應消息都是 “Yes”,則向參與者發送 “DoCommit” 消息,參與者完成本地事務的其他操作并釋放資源,然后向協調者發送 “HaveCommitted”消息;
如果協調者收到的消息中包含“No”消息或者在規定時間內有參與者沒有響應,則向所有參與者發送“DoAbort”消息,此時發送“Yes”的參與者則會根據之前執行操作時的回滾日志對操作進行回滾,然后所有參與者會向協調者發送“HaveCommitted”消息;
提交階段的流程,大概入下圖所示:
二階段提交提交階段二階段提交協議容易理解,基于 XA 的二階段提交算法滿足事務的 ACID 特性,看上去比較完美,但是缺點還是挺多的,主要有以下幾個問題:
同步阻塞問題:二階段提交在執行過程中,所有參與節點都是事務阻塞型的,參與者會鎖定數據,其他訪問者要訪問該數據的話,都會被阻塞。
單點故障問題。在二階段提交協議中,協調者只有一臺,一旦協調者發送故障,整個系統都會處于停滯階段。特別是提交階段,如果協調者掛了的話,參與者就會一直等待協調者回應,會處于阻塞中。
數據不一致問題:在提交階段,協調者向參與者發送 DoCommit 請求后,由于網絡抖動或者在發送請求的過程中,協調者發生故障,就會導致只有一部分參與者接收到了提交請求并執行提交操作,但其他未接到提交請求的那部分參與者則無法執行事務提交。于是整個分布式系統便出現了數據不一致的問題。
三階段提交
三階段提交協議(Three-phase commit protocol,3PC)是對二階段提交(2PC)的改進。解決了二階段提交的一些問題,三階段和二階段提交最大的不同是引入超時機制和準備階段。
先來說說超時機制,在二階段提交,只有協調者才有超時機制,如果協調者在規定時間內沒有接收到參與者的響應,就會根據當前狀態提交或者終止整個事務,但是如果協調者掛了,參與者并沒有超時機制,所以就一直等待,這也是二階段提交單點故障的問題。在三階段提交中,同時在協調者和參與者中引入超時機制。如果協調者或參與者在規定的時間內沒有接收到來自其他節點的響應,就會根據當前的狀態選擇提交或者終止整個事務。
三階段提交其實就是將二階段提交中的提交階段一分為二,三階段提交協議中的具體三階段是:CanCommit、PreCommit、DoCommit 三個階段
CanCommit 階段,CanCommit 階段與 2PC 的投票階段類似:協調者向參與者發送請求操作(CanCommit 請求),詢問參與者是否可以執行事務提交操作,然后等待參與者的響應;參與者收到 CanCommit 請求之后,回復 Yes,表示可以順利執行事務;否則回復 No。
PreCommit 階段,根據二階段提交中的提交階段相似,根據 CanCommit 階段返回的結果,來決定是否可以進行 PreCommit 操作。
圖片描述這時候就存在兩種情況,如果所有參與者都回復 “Yes”,那么執行流程是這樣的:
1、協調者發送預提交請求:協調者向參與者發送 PreCommit 請求,進入預提交階段.
2、事務預提交:參與者接收到 PreCommit 請求后執行事務操作,并將 Undo 和 Redo 信息記錄到事務日志中。
3、響應反饋:如果參與者成功執行了事務操作,則返回 ACK 響應,同時開始等待最終指令。
如果有參與者返回 “No”,或者協調者在規定時間內沒有收到參與者的響應,那么將執行中斷事務操作。流程是這樣的:
1、發送中斷請求:協調者向所有參與者發送“Abort”消息。
2、中斷事務:參與者收到“Abort”消息之后,或超時后仍未收到協調者的消息,執行事務的中斷操作。
DoCommit 階段, 事務真正提交階段,協調者根據 PreCommit 階段參與者返回來的信息,決定是進入提交階段還是事務中斷階段。
圖片描述提交階段流程如下:
1、發送提交請求:協調者接收到所有參與者發送的 Ack 響應,從預提交狀態進入到提交狀態,并向所有參與者發送 DoCommit 消息。
2、事務提交:參與者接收到 DoCommit 消息之后,正式提交事務。完成事務提交之后,釋放所有鎖住的資源。
3、響應反饋:參與者提交完事務之后,向協調者發送 Ack 響應。
4、完成事務:協調者接收到所有參與者的 Ack 響應之后,完成事務。
事務中斷階段,流程如下:
1、發送中斷請求:協調者向所有參與者發送 Abort 請求。
2、事務回滾:參與者接收到 Abort 消息之后,利用其在 PreCommit 階段記錄的 Undo 信息執行事務的回滾操作,并釋放所有鎖住的資源。
3、反饋結果:參與者完成事務回滾之后,向協調者發送 Ack 消息。
4、中斷事務:協調者接收到參與者反饋的 Ack 消息之后,執行事務的中斷,并結束事務。
基于分布式消息的最終一致性方案
不管是二階段提交還是三階段提交,都屬于強一致性的,滿足事務的 ACID 原則。它們都有兩個共同的問題:
1、需要鎖定數據,降低了系統性能。
2、因為網絡等原因,并沒有完全解決數據一致性問題。
而基于 MQ 消息的分布式解決方案就不太一樣了,它采用的不是強一致性,而是最終一致性,這也就是 BASE 理論。并且我們直到 MQ 是異步的,所以性能也比較快,可以說完美的解決了上面兩種方式帶來的問題。
基于 MQ 消息中間件解決分布式事務的思路是這樣的:主要是基于 MQ 消息投遞的可靠性,將分布式事務發送給 MQ 中間件之后,中間件將事務持久化,這一點非常重要,保證消息不丟失。消費者端異步消費,如果遇到失敗情況,由于我們的消息是持久化的,所以可以根據業務規則不斷重試,有必要的話,人工補償,保證數據最終一致性。
關于基于分布式消息的最終一致性方案,我準備基于 RocketMQ 單獨開一個章節,詳細聊一聊,這里就不多說了。
本人在這幾年及春招的總結,歷時3個月,我覺得很全面了,對于面試很有幫助,目前,本人已經拿到了騰訊等大廠offer,進入到大廠不是夢想,github 地址:
https://github.com/OUYANGSIHAI/JavaInterview
這么辛苦總結,給個star好不好。?點擊閱讀原文,直達
總結
以上是生活随笔為你收集整理的漫谈分布式事务的那些解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大牛都会用的 IDEA 调试技巧!!!
- 下一篇: 拜托,别再问我什么是堆了!