分佈式事務與2PC、3PC理論詳解

事務概念

大部分情況下我們所說的事務都是數據庫事務(Database Transaction),後來延時到了非關係型數據庫等其他領域,事務是運行在我們數據庫上的一個邏輯工作單元,運行在工作單元中的所有sql都具有原子性的操作特點。

數據庫事務要滿足ACID(更詳細查看相關文章):

A:Atomic,原子性,事務必須是原子的工作單元,一個事務裏面的所有操作要麼全部成功,要麼全部失敗。
C:Consistency:一致性,事務完成時,保證所有數據的狀態是一致性的。比如轉賬業務,有五個賬戶之間相互轉賬,無論轉賬多少次,怎麼轉賬,五個賬戶的總餘額都必須是不變的。
I:Isolation:隔離性,併發事務對同一資源所做的修改,事務之間所做的修改時隔離的,通過鎖等手段實現。
D:Duration:持久性,事務完成之後,對系統的影響是永久性的,直到另一個事務執行改變其狀態。

隔離性是通過鎖實現的。
原子性、一致性、持久性是通過一些記錄,比如事務日誌、IO等實現。事務日誌redo表示事務修改之後的狀態記錄,undo事務日誌表示事務修改之前的狀態記錄,事務提交使用redo,事務回滾使用undo。

分佈式事務

當你的系統是單機系統,並且使用單一數據庫時,利用傳統關係型數據庫的事務特性來實現事務控制非常簡單。但是,當你進行數據庫分庫分表、系統SOA話之後,傳統關係型數據庫的事務特性就不能滿足事務需求了。
分佈式事務指的是分佈式環境下的事務。。。。

分佈式事務產生的原因
  1. 數據庫分庫分表
    當系統數據量達到一定規模時,就要對數據庫進行分庫分表,本來一個數據庫就會分爲多個數據庫實例,拆分以後,一個操作可能就要對多個數據庫實例進行事務操作。
    在這裏插入圖片描述
    應用系統有個操作需要對DB1修改一條數據,並且需要在DB2中新增一條數據,此時這兩個操作要在同一個事務單元中,此時,就產生了一個分佈式事務。
應用系統SOA化

原本的單體系統,單數據庫,配合spring的聲明式事務加上數據庫的事務特性可以很容易實現事務控制,但是,當業務量達到一定程度後,需要對系統SOA話。比如原本的一個電商系統,拆分成如下幾個子系統。
在這裏插入圖片描述
此時一個生成訂單的操作,分爲兩步,訂單表新增一條記錄,庫存表某條記錄庫存數減庫存。這個操作應當在同一事務上進行,並且此時兩個操作處於不同系統、不同數據庫中,此時就產生了一個分佈式事務。

2PC提交協議與3PC提交協議

2PC和3PC都是強一致性的協議。

2PC協議

2PC(Two Phase Commit),二階段提交,是計算機網絡,尤其是在數據庫領域內,爲了使得基於分佈式系統架構下所有節點在進行事務處理過程中能夠保持原子性和一致性設計的一種算法。目前,絕大部分關係型數據庫都是採用二階段提交協議來完成分佈式事務的處理,利用該協議能夠非常方便地完成所有分佈式事務參與者的協調,同一決定事務的提交或者回滾。從而能夠有效地保證分佈式事務的一致性,因此二階段提交協議被廣泛應用到許多分佈式系統中。
二階段提交就是把事務的提交分爲兩個階段來執行。

階段一:提交事務請求
  1. 協調者像所有參與者發送事務內容,詢問是否可以開始事務的操作,並等待個參與者的響應。
  2. 各參與者節點執行事務操作,並記錄相應的事務日誌Undo、Redo。
  3. 各參與者反饋給協調者事務的執行情況,執行成功返回yes、執行失敗分會no。

在這裏插入圖片描述

階段二:執行事務提交

協調者會根據參與者階段一的反饋決定是提交事務還是回滾事務。

  • 假設參與者全部都成功執行事務成功,返回yes,那麼協調者就會像所有參與者發出Commit請求。
  • 參與者接收到Commit請求後,會正式進行事務的提交,並再事務完成提交後釋放事務佔用的資源。
  • 參與者在完成事務提交後,向參與者發送ack確認信息。
  • 協調者接收到所有參與者反饋的ACK消息後,完成事務。

在這裏插入圖片描述

假設參與者有一個或者一個以上返回NO。

  • 協調者向所有參與者發送回滾事務請求。
  • 參與者接收到回滾事務請後,會利用在階段1中記錄的undo事務日誌信息來執行事務的回滾操作,並在事務回滾完成後釋放事務佔用的資源。
  • 參與者在事務回滾之後,像協調者返回ACK確認信息。
  • 協調者接收到所有ACK之後,完成事務中斷。
    在這裏插入圖片描述
    在兩個階段中,階段一因爲要涉及到記錄事務日誌,磁盤IO等耗時操作,所以2PC提交的時間消耗階段一佔據着絕大佔比,而階段二的操作相比起來就快很多,相當於一個非常短的時間操作。那麼階段二發生錯誤的概率會比階段一小非常多,所以只要階段一成功了,階段二發生錯誤的概率是非常小的,因爲階段二是一個瞬時操作。那麼就可以大大增加分佈式事務的成功率。

優點:原理簡單、實現方便。
缺點:同步阻塞、單點問題、腦裂、太過保守。

同步阻塞:
二階段提交最明顯也是最大的一個問題是同步阻塞問題,這會極大限制了分佈式系統的性能,在二階段事務的執行過程中,所有參與者的邏輯都處於阻塞狀態,也就是說各個參與者在等待其他參與者響應的過程中,都處於阻塞狀態,佔用着資源。在階段1,可能某些節點的事務執行得快,所以向協調接反饋得快,某些節點的事務執行得慢,所以向協調接反饋得慢,而協調者要接收到所有參與者的反饋纔會執行階段二操作,此時事務執行得快的參與者即使事務已經執行完了並作出了反饋,依然會阻塞等待階段二的來臨。

單點問題:
一旦協調者出現問題,那麼整個二階段事務提交將無法進行,更爲嚴重的是如果協調者在階段二中出現問題,比如來不及向參與者發事務提交請求就宕機了,那麼所有參與者都會處於事務資源的鎖定當中,而無法完成事務操作。這個也是屬於同步阻塞問題。

數據不一致:
在階段二時,協調者向參與者發送Commit提交請求時,發生局部網絡異常(協調者與部分參與者無法通信)或者信息還沒向全部參與者發送(只發送了部分)就突然宕機時,導致只有部分參與者收到了Commit請求,收到了Commit請求的參與者進行事務提交,而沒有收到的參與者無法進行事務提交,於是整個分佈式系統便出現了數據不一致性現象。

太過保守:
如果協調者指示參與者進行事務提交詢問過程中,參與者出現故障而導致協調者始終無法獲取到所以參與者的響應信息的話,這時,協調者只能依靠自身的超時機制判斷是否需要中斷事務。這樣的策略顯得比較保守,換句話說2階段提交協議沒有設計較爲完善的容錯機制。任意一節點的失敗都會導致整個事務的失敗。

3PC提交協議(Three Phase Commit)

基於2PC提交協議遇到的問題,出現了3PC提交協議。即三階段提交,把二階段提交的階段一“提交事務請求”拆分成兩個階段,形成由CanCommit、PreCommit、doCommit三個階段組成的分佈式事務處理協議。

階段一:Can Commit
  • 事務詢問:協調者向所有的參與者發送一個包含事務內容的canCommit請求,試問參與者是否可以執行事務的提交操作,並開始等待各參與者的響應。
  • 各參與者像協調者反饋事務詢問響應,參與者在接收到協調者的canCommit請求後,就判斷自身是否可以進行該事務操作(判斷的邏輯實現者自行確定,也許是僅僅判斷參與者自身有沒有宕機和與協調者的網絡是否正常,也有可能還要根據事務資源狀態來判斷),經過判斷後,如果認爲自身可以執行事務的,就向協調者返回yes響應。否則返回No響應。
階段二:Pre Commit

在階段二,協調者會根據參與者在階段一的反饋情況來決定是否可以進行事務的Pre Commit操作。包含兩種可能:

  1. 在階段一所有參與者都返回了Yes反饋,表示都可以執行事務操作。就進行事務預提交:
  • 協調者向所有參與者發送事務與提交請求PreCommit,並進入Prepared階段。
  • 參與者接收到PreCommit請求後,會執行事務操作,並記錄事務日誌Redo、UnDo信息。
  • 如果參與者成功執行了事務,就會反饋給協調者ACK響應,同時等待最終指令(提交/回滾)。
  1. 在階段一中,假設有一個或一個以上的參與者返回了No信息,或者在超時時間內協調者沒有接收到所有參與者的反饋。那麼就會中斷事務。
  • 協調者向所有參與者發送中斷請求(abort請求)。
  • 無論是收到來自協調者的abort請求,還是在等待協調者消息時出現超時時,參與者都會執行中斷事務操作。
階段三:(Do Commit)

該階段進行事務的真正提交。也會有兩種情況出現。

  1. 在階段三,假設協調者在超時時間內接收到了來自所有參與者的ACK反饋,就會進行事務提交。
  • 協調者從預提交狀態轉換到提交狀態,並向所有參與者發送doCommit請求。
  • 參與者接受到doCommit請求後,會正式進行事務的提交操作,並再完成事務提交之後釋放在整個事務期間佔用的事務資源。
  • 參與者在完成事務提交之後。向協調者發送ACK消息。
  • 協調者接收到所有參與者的ACK消息之後,完成事務。
  1. 在階段三,假設有一個或者一個以上的參與者沒有返回給協調者ACK或者協調者在超時時間內沒有收到來自所有參與者的ACK反饋,就進入事務中斷狀態。
  • 發送中斷請求,協調者向所有參與者發送abort請求。
  • 參與者在接收到abort請求後,會利用階段二記錄的Undo事務日誌進行回滾操作,並再回滾之後釋放整個事務執行期間佔用的資源。
  • 參與者在完成事務回滾操作後,向協調者反饋ACK消息。
  • 協調者接收到所有參與者反饋的ACK消息後,中斷事務。

注意:
跟2PC有一個明顯不同點是,2PC只有協調者有超時機制,參與者是沒有超時機制的,也就是2PC時在階段二參與者沒有收到協調者的Commit請求會一直阻塞,佔用事務資源。
3PC提交,參與者也會有超時機制,在階段三時,假設協調者宕機或者協調者與參與者之間出現網絡問題,最終導致參與者無法及時接收來自協調的在第三階段的doCommit或者abort請求或者第二階段的preCommit請求時,參與者在等待超時後,會進行事務提交操作。

優點:
3PC解決了2PC的前兩個問題,即阻塞和單點問題。但是還是沒能解決數據一致性問題。但是進一步減小了數據不一致的概率。

解決阻塞問題是減少了參與者的阻塞範圍,這個優化點,主要是避免了參與者在長時間無法與協調者節點通訊(協調者掛掉了)的情況下,無法釋放資源的問題,因爲參與者自身擁有超時機制會在超時後,自動進行本地commit從而進行釋放資源。而這種機制也側面降低了整個事務的阻塞時間和範圍。

解決了單點問題:因爲參與者在超時後會自動進行本地commit從而進行釋放資源。所以當協調者宕機或者與參與者之間出現網絡故障時,並且提交事務並釋放資源,假設所有參與者都沒有接收到協調者的階段三的請求時,或者只接收了doCommit請求時,事務也能保持一致,因爲接收到的參與者會執行提交操作,超時的參與者也會執行提交操作。

但是3PC也會出現數據一致性問題:
在階段三:假設階段二時,有一個或者一個以上的參與者反饋給協調者ACK反饋,或者協調者在超時時間內沒有收到所有參與者的ACK反饋,協調者在階段三就會向參與者發送abort請求,但是由於協調者與部分參與者存在網絡故障或者協調者在發送給了部分參與者abort請求後就宕機了,導致部分參與者由於超時而提交事務,部分參與者由於接收到了abort請求而回滾事務,這就導致了不一致問題。

減小了數據不一致的概率:
在2PC提交協議,在第二階段無論協調者發送Commit請求還是abort請求給參與者,只要出現由於協調者與部分參與者存在網絡故障或者協調者在發送給了部分參與者abort請求後就宕機了的情況,就會導致數據不一樣。
在3PC提交協議,在第三階段,如果出現由於協調者與部分參與者存在網絡故障或者協調者在發送給了部分參與者abort請求後就宕機了的情況的這個情況,當能接收到協調者消息的部分參與者接收到的消息是doCommit消息,數據還是能夠保持一致的,因爲超時的參與者也是執行提交操作,只有當協調者發送給部分參與者的消息是abort請求時,纔會出現數據不一致。

以上兩個協議都不能完全解決數據一致性問題,還得通過一些補償機制來實現事務一致性。

分佈式事務的解決方案

X/OpenDTP事務模型:
X/OpenDTP(X/Open Distributed Transaction Procession Reference Model):是X/Open這個組織定義的一套分佈式事務的標準,也就是定義了規範的API接口,由各個廠商進行具體實現。
這個標準提出了使用二階段提交來保證分佈式事務的完成性,後來J2EE也遵循了這套規範,設計並實現了java裏的分佈式事務編程接口規範-JTA。

X/A事務是 X/Open DTP定義的中間件與數據庫之間的接口規範。X/A接口函數由數據庫廠商提供。

X/OpenDTP事務模型的角色:
X/OpenDTP事務模型有三個角色:
AP:Application,也就是我們的應用系統。
RM:Resources Manager,資源管理器,也就是我們的數據庫。
TM:Transaction Manager,事務管理器、事務協調者。負責分佈式事務的協調。

流程就與2PC提交一樣。AP類比參與者,TM類比協調者。

Java X/OpenDTP事務模型的實現:

  • JOTM(Java Open Transaction Manager):基於javaee JTA規範實現的。
  • Atomikos ,原本是一個商業項目,後來開源了。

這篇就不展開講這兩個了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章