在講述分散式事務的概念之前,我們先來回顧下事務相關的一些概念。
就是一個程式執行單元,裡面的操作要麼全部執行成功,要麼全部執行失敗,不允許只成功一半另外一半執行失敗的事情發生。例如一段事務程式碼做了兩次資料庫更新操作,那麼這兩次資料庫操作要麼全部執行成功,要麼全部回滾。
我們知道事務有4個非常重要的特性,即我們常說的(ACID)。
其實分散式事務從實質上看與資料庫事務的概念是一致的,既然是事務也就需要滿足事務的基本特性(ACID),只是分散式事務相對於本地事務而言其表現形式有很大的不同。
本地事務的時代,如果需要同時運算元據庫的多條記錄,而這些操作可以放到一個事務中,那麼我們可以通過資料庫提供的事務機制就可以實現。
而隨著微服務架構的推進,原本一個本地邏輯執行單元,被拆分到了多個獨立的微服務中,這些微服務又分別操作了不同的資料庫和表。
比如下個指派個體的運輸任務,下運輸任務的同時要生成需求、計劃、任務,還要去呼叫詢價服務,投保服務,那麼,一旦產生任何一個服務異常,都會產生事務性的問題。雖然對於我們現有邏輯來說,可以由運營作廢,但未來自動化之後呢?
分散式事務是為了解決微服務架構(形式都是分散式系統)中不同節點之間的資料一致性問題。這個一致性問題本質上解決的也是傳統事務需要解決的問題,即一個請求在多個微服務呼叫鏈中,所有服務的資料處理要麼全部成功,要麼全部回滾。當然分散式事務問題的形式可能與傳統事務會有比較大的差異,但是問題本質是一致的,都是要求解決資料的一致性問題。
而分散式事務的實現方式有很多種,最具有代表性的是由Oracle Tuxedo系統提出的XA分散式事務協定。XA協定包括兩階段提交(2PC)和三階段提交(3PC)兩種實現,接下來我們分別來介紹下這兩種實現方式的原理。
兩階段提交又稱2PC(two-phase commit protocol),2PC是一個非常經典的強一致、中心化的原子提交協定。這裡所說的中心化是指協定中有兩個角色:一個是分散式事務協調者(coordinator)和N個參與者(participant)。
兩階段提交,顧名思義就是要進行兩個階段的提交:第一階段,準備階段(投票階段);第二階段,提交階段(執行階段)。
1)如果所有參與者均反饋的是成功,協調者就會向所有參與者傳送「全域性提交確認通知(global_commit)」,參與者Participant就會完成自身本地資料庫事務的提交,並將提交結果回覆「ack」訊息給協調者Coordinator,然後協調者Coordinator就會向呼叫方返回分散式事務處理完成的結果。如果有任何一個參與者返回失敗,則回滾事務。
2)如果參與者向協調者反饋「Vote_Abort」訊息,即返回了失敗的訊息。此時分散式事務協調者Coordinator就會向所有的參與者Participant發起事務回滾的訊息(「global_rollback」),此時各個參與者就會回滾本地事務,釋放資源,並且向協調者傳送「ack」確認訊息,協調者就會向呼叫方返回分散式事務處理失敗的結果。
以上就是兩階段提交的基本過程了,那麼按照這個兩階段提交協定,分散式系統的資料一致性問題就能解決麼?
其實,2PC只是通過增加了事務協調者(Coordinator)的角色來通過2個階段的處理流程來解決分散式系統中一個事務需要跨多個服務的資料一致性問題。
以下幾點是XA-兩階段提交協定中會遇到的一些問題:
三階段提交又稱3PC,在2PC的基礎上增加了CanCommit階段,並引入了超時機制。一旦事務參與者遲遲沒有收到協調者的Commit請求,就會自動進行本地commit,這樣相對有效地解決了協調者單點故障的問題。
相比較2PC而言,3PC對於協調者(Coordinator)和參與者(Participant)都設定了超時時間,解決了參與者在長時間無法與協調者節點通訊(協調者掛掉了)的情況下,無法釋放資源的問題,因為參與者自身擁有超時機制會在超時後,自動進行本地commit從而進行釋放資源。而這種機制也側面降低了整個事務的阻塞時間和範圍。
另外,通過CanCommit、PreCommit、DoCommit三個階段的設計,相較於2PC而言,多設定了一個緩衝階段保證了在最後提交階段之前各參與節點的狀態是一致的。
3PC的缺點:
3PC在去除阻塞的同時也引入了新的問題,那就是參與者接收到precommit訊息後,如果出現網路分割區,此時協調者所在的節點和參與者無法進行正常的網路通訊,在這種情況下,該參與者依然會進行事務的提交,這必然出現資料的不一致性。
TCC與2PC、3PC一樣,只是分散式事務的一種實現方案。
TCC(Try-Confirm-Cancel)又稱補償事務。其核心思想是:」針對每個操作都要註冊一個與其對應的確認和補償(復原操作)」。它分為三個操作:
TCC事務的處理流程與2PC兩階段提交類似,不過2PC通常都是在跨庫的DB層面,而TCC本質上就是一個應用層面的2PC,需要通過業務邏輯來實現。這種分散式事務的實現方式的優勢在於,可以讓應用自己定義資料庫操作的粒度,使得降低鎖衝突、提高吞吐量成為可能。
而不足之處則在於對應用的侵入性非常強,業務邏輯的每個分支都需要實現try、confirm、cancel三個操作。此外,其實現難度也比較大,需要按照網路狀態、系統故障等不同的失敗原因實現不同的回滾策略。為了滿足一致性的要求,confirm和cancel介面還必須實現冪等。
TCC的具體原理圖如下:
接入TCC前,業務操作只需要一步就能完成,但是在接入TCC之後,需要考慮如何將其分成2階段完成,把資源的檢查和預留放在一階段的Try操作中進行,把真正的業務操作的執行放在二階段的Confirm操作中進行;
TCC服務要保證第一階段Try操作成功之後,二階段Confirm操作一定能成功;
事務協調器在呼叫TCC服務的一階段Try操作時,可能會出現因為丟包而導致的網路超時,此時事務協調器會觸發二階段回滾,呼叫TCC服務的Cancel操作;
TCC服務在未收到Try請求的情況下收到Cancel請求,這種場景被稱為空回滾;TCC服務在實現時應當允許空回滾的執行;
事務協調器在呼叫TCC服務的一階段Try操作時,可能會出現因網路擁堵而導致的超時,此時事務協調器會觸發二階段回滾,呼叫TCC服務的Cancel操作;在此之後,擁堵在網路上的一階段Try封包被TCC服務收到,出現了二階段Cancel請求比一階段Try請求先執行的情況;
使用者在實現TCC服務時,應當允許空回滾,但是要拒絕執行空回滾之後到來的一階段Try請求;
無論是網路封包重傳,還是異常事務的補償執行,都會導致TCC服務的Try、Confirm或者Cancel操作被重複執行;使用者在實現TCC服務時,需要考慮冪等控制,即Try、Confirm、Cancel 執行次和執行多次的業務結果是一樣的;
TCC服務的一階段Try操作會做資源的預留,在二階段操作執行之前,如果其他事務需要讀取被預留的資源資料,那麼處於中間狀態的業務資料該如何向用戶展示,需要業務在實現時考慮清楚;通常的設計原則是「寧可不展示、少展示,也不多展示、錯展示」;
TCC服務的一階段Try操作預留資源之後,在二階段操作執行之前,預留的資源都不會被釋放;如果此時其他分散式事務修改這些業務資源,會出現分散式事務的並行問題;
使用者在實現TCC服務時,需要考慮業務資料的並行控制,儘量將邏輯鎖粒度降到最低,以最大限度的提高分散式事務的並行性;
Hmily (How much I love you)
高效能分散式事務tcc開源框架。基於java語言來開發(JDK1.8),支援dubbo,springcloud,motan等rpc框架進行分散式事務。
框架特性
原理圖:
流程圖:
作者:京東物流 宋樂
來源:京東雲開發者社群 自猿其說Tech 轉載請註明來源