分散式強一致性事務
一.事務的概念
事務是一組操作的執行單元,相對於資料庫操作來講,事務管理的是一組SQL指令,比如增加,修改,刪除等,事務的一致性,要求,這個事務內的操作必須全部執行成功,如果在此過程種出現了差錯,比如有一條SQL語句沒有執行成功,那麼這一組操作都將全部回滾
最經典的例子便是:A向B匯款500元,B賬戶多了500元,這整個過程,要麼全部正常執行,要麼全部回滾,不然就會出現A扣款,B收不到錢,或者A沒扣款,B收到500元的情況,這種場景是災難性的。
事務特性(ACID)
- Atomic(原子性):要麼都成功,要麼都失敗
- Consistent(一致性):資料應該不被破壞
- Isolate(隔離性):使用者間操作不相混淆
- Durable(永續性):永久儲存
二.本地事務和分散式事務
本地事務:本地事務是在同一個JVM例項中呼叫不同的資源形成事務,緊密依賴於底層資源管理器(例如資料庫連線 ),事務處理侷限在當前事務資源內。此種事務處理方式不存在對其他應用伺服器或例項的依賴。
如上述說到的匯款案例來說,若兩個過程都在同一個JVM例項中,使用同個資料庫連線,則事務比較好控制,如下圖:
分散式事務:分散式事務是在一個JVM例項中呼叫不同的服務形成事務,不同的服務可能不在同一個JVM例項中,可能涉及到操作多個數據庫的事務,必須保證每個獨立節點上的資料操作可以滿足ACID。
如上述說到的匯款案例來說,若兩個過程在不同的JVM例項中,使用不同的資料庫連線,則事務不好控制,原因是A,B無法知道事務在其他地方所做的所有動作,無法實現事務的一致性,如下圖,A,B各自使用自己的事務管理完成begin,commit,rollback等操作,AB整個過程是無法實現事務一致性的。
三.對分散式事務的處理
在分散式系統中,各個節點之間在物理上相互獨立,通過網路進行溝通和協調。但是相互獨立的節點無法知道其他節點的事務執行情況,要保證所有節點上的寫操作,要麼全部提交,要麼全部rollback,最好的辦法就是引入coordinator,即協調者來協調和通知各個節點上的事務執行情況。
Java 事務程式設計介面(JTA:Java Transaction API)和 Java 事務服務 (JTS:Java Transaction Service) 為 J2EE 平臺提供了分散式事務服務。分散式事務(Distributed Transaction)包括事務管理器(Transaction Manager)和一個或多個支援 XA 協議的資源管理器 ( Resource Manager )。我們可以將資源管理器看做任意型別的持久化資料儲存;事務管理器承擔著所有事務參與單元的協調與控制。
由於事務管理器統一管理所有事務參與單元,則所有事務參與單元都應實現同一套協議,才方便事務管理器的管理,就好似一個老師A管理一班學生,所有學生按照老師約定好的形式完成作業,之後交由同一個老師批改,批改後統一退回。若同一個作業,部分學生按照A老師的標準完成,部分同學按照B老師的標準完成,再提交上去批改,則最終得到的結果就可能互相不一致了。
四.XA協議
分散式事務中,所有的事務單元都需要實現統一的協議,XA 就是定義的交易中介軟體與資料庫之間的介面規範(即介面函式),交易中介軟體用它來通知資料庫事務的開始、結束以及提交、回滾等。 XA 介面函式由資料庫廠商,或訊息中介軟體廠商提供。
如下圖,我們可以將 JTA 的事務管理器和資源管理器理解為兩個方面:面向開發人員的使用介面(事務管理器)和麵向服務提供商的實現介面(資源管理器),開發人員使用開發人員介面,實現應用程式對全域性事務的支援;各提供商(資料庫,訊息中介軟體等)依據提供商介面(即XA介面)的規範提供事務資源管理功能;事務管理器( Transaction Manager )將應用對分散式事務的使用對映到實際的事務資源並在事務資源間進行協調與控制,使得 JTA 可以在異構事務資源之間執行協同溝通。
五.強一致性事務實現原理
2PC,即二階段提交,是分散式事務中一個很重要的協議,當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個coordinator,即協調者作為的元件來統一掌控所有節點(稱作參與者)的操作結果並最終指示這些節點是否要把操作結果進行真正的提交或回滾。
二階段提交的演算法思路可以概括為: 參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是回滾操作。
各參與者成功提交事務流程:
1.在一個有A,B,C參與的事務中,引入一個協調者,用來協調和通知各參與者執行相應的事務動作,其中黑線為協調者發起的指令,紅線為參與者響應的指令。
2.協調者通知所有參與者節點準備提交事務,參與者節點執行事務操作,並將Undo資訊和Redo資訊寫入日誌,但此時不提交事務。
3.協調者收到各參與者準備提交完成的指令,沒有任何節點出錯,此時通知所有節點提交事務。
4.參與者節點正式完成提交操作,並釋放在整個事務期間內佔用的資源。
5.協調者收到所有參與者確認提交完畢的答覆,此時整個事務完成。
各參與者提交事務失敗流程:
1.在一個有A,B,C參與的事務中,引入一個協調者,用來協調和通知各參與者執行相應的事務動作,其中黑線為協調者發起的指令,紅線為參與者響應的指令。
2.協調者通知所有參與者節點準備提交事務,參與者節點執行事務操作,並將Undo資訊和Redo資訊寫入日誌,但此時不提交事務,但此時B節點在準備執行操作事務時出現了異常,則返回給協調者準備失敗的響應資訊
3.協調者雖然收到A跟C準備完成的響應,但B返回的狀態是準備失敗,此時進入通知所有參與者回滾的階段
4.參與者節點正式完成回滾操作,並釋放在整個事務期間內佔用的資源。
5.協調者收到所有參與者確認回滾完畢的答覆,此時整個事務完成
結論:不管最後結果如何,第二階段都會結束當前事務。
建議:少使用分散式事務,在分散式事務這個問題上,還很少有成熟牛逼的產品,而且分散式事務過程中,涉及到了各個節點的通知,二次通知,當節點多的時候,協調者的壓力巨大,而且整個流程對業務的時間開銷是巨大的,所以建議謹慎使用分散式事務,即使二階段看似能處理好分散式節點的ACID問題,但是其本身也存在不小的問題。
1.同步阻塞問題。執行過程中,所有參與節點都是事務阻塞型的。當參與者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態。
2.單點故障。由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因為協調者宕機導致的參與者處於阻塞狀態的問題)
3.資料不一致。在二階段提交的階段二中,當協調者向參與者傳送commit請求之後,發生了局部網路異常或者在傳送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分散式系統便出現了資料部一致性的現象。
4.二階段無法解決的問題:協調者再發出commit訊息之後宕機,而唯一接收到這條訊息的參與者同時也宕機了。那麼即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。
本文版權歸作者所有,歡迎轉載,請務必新增原文連結。