XA 分佈式事務研究

在開始講解XA事務前,先引出一個例子來說解這樣比較容易理解XA事務。好比有一筆交易,在交易完成後,接受到到交易成功信息和扣款成功信息,代碼以下:html

public void savePayOrder(PayOrder payOrder) throws Exception { 
     try { 
         ...//交易前預備邏輯 
         PayOrderResult payOrderResult=  payOrderService.save(payOrder);
         noticeService.excuteNotice(payOrderResult); 
     } catch (PayOrderExecutionException e) { 
         logger.error(e); 
         sessionCtx.setRollbackOnly(); 
         throw e; 
     } 
 }

在開頭首先查詢了一下訂單相關的業務參數,而後先保存交易信息,而後再更新相關信息,而這個過程須要操做多個表,最後達成交易。注意這裏有可能在保存一筆交易的時候,就拋出異常,致使後面沒法更新交易的相關信息。java

若是順利的話,這段代碼能夠保證遵循ACID準則數據庫

這時候若是咱們加入一個新需求,好比須要交易成功後發送一條信息通知用戶交易成功,而且send()方法裏實現全部消息邏輯,則例子改爲以下:segmentfault

public void savePayOrder(PayOrder payOrder) throws Exception { 
     try { 
         ...//交易前預備邏輯 
         PayOrderResult payOrderResult=  payOrderService.save(payOrder);
         smsService.send(payOrderResult);//發送短信
         noticeService.excuteNotice(payOrderResult); 
     } catch (PayOrderExecutionException e) { 
         logger.error(e); 
         sessionCtx.setRollbackOnly(); 
         throw e; 
     } 
 }

這段代碼卻不會保證ACID準則。若是excuteNotice()方法拋出了PayOrderExecutionException,數據庫更改將會回滾,但交易後發出的消息將會經過send()方法被髮送到JMS進行相關的訂閱或發送。網絡

在非XA環境中,消息隊列的插入過程獨立於數據庫更新操做,ACID準則中的原子性和獨立性不能獲得保證,從而總體上數據完整性受到損害。咱們須要的是,有一種方式可以讓消息隊列和數據庫處於單一事務的控制之下,以致於兩個資源能被協調造成單一工做單元。使用X/Open的XA接口,咱們便可以作到協調多個資源,保證維持ACID準則。session

XA接口詳解

XA接口是雙向的系統接口,分佈式事務是由一個一個應用程序(Application Program)、一個事務管理器(Transaction Manager)以及一個或多個資源管理器(Resource Manager)之間造成通訊橋樑。事務管理器控制着JTA事務,管理事務生命週期,並協調資源。併發

JTA中,事務管理器抽象爲javax.transaction.TransactionManager接口,並經過底層事務服務(即JTS)實現。資源管理器負責控制和管理實際資源(如數據庫或JMS隊列)。下圖說明了事務管理器、資源管理器,以及典型JTA環境中客戶端應用之間的關係:分佈式

圖片描述

XA分佈式事務是由一個或者多個Resource Managerd,一個事務管理器Transaction Manager以及一個應用程序 Application Program組成。性能

  • 資源管理器:提供訪問事務資源的方法,一般一個數據庫就是一個資源管理器。ui

  • 事務管理器:協調參與全局事務中的各個事務。須要和參與全局事務中的資源管理器進行通訊。

  • 應用程序:定義事務的邊界,指定全局事務中的操做。

XA使用場景

許多事務管理器採用這種單階段提交的模式,能夠避免單一事務資源下的過分開銷,以及性能的降低,若是在不適合的場景中引入XA數據庫驅動,特別是資源比較侷限的狀況下使用本地事務模型(Local Transaction Model)。

那究竟什麼狀況下使用XA事務呢?

通常來講,當你的上下邏輯結構涉及的表或者須要協調的資源(如數據庫,以及消息主題或隊列等)比較多的時候,建議使用XA。

或者對於該系統在將來對整個結構模塊趨於穩定,要求負載、代碼擴展等方面穩定性大於性能,則可選擇XA。

若是這些資源並不在同一個事務中使用,就沒有必要去用XA。

而對於性能要求很高的系統,建議使用 一階段提交(Best Efforts 1PC)事務補償機制

二階段提交(The two-phase commit protocol,2PC)

二階段提交是分佈式事務的重要的一個關鍵點,二階段提交協議包含了兩個階段:第一階段(也稱準備階段)和第二階段(也稱提交階段)。

引用《Java事務設計策略》一圖
引用《Java事務設計策略》一圖

1. 準備階段:準備階段,每一個資源管理器都會被輪訓一遍,事務管理器給每一個資源管理器發送Prepare消息,每一個資源管理器要麼直接返回失敗(如權限驗證失敗)或異常,要麼在本地執行事務等等,但不Commoit,處於Ready狀態。

2. 提交階段:若是事務管理器收到了資源管理器的失敗信息(如異常、超時等),直接給每一個資源管理器發送回滾(Rollback)消息;不然,發送提交(Commit)消息;資源管理器根據事務管理器的指令執行Commit或者Rollback操做,釋放全部事務處理過程當中使用的鎖資源。(注意:必須在最後階段釋放鎖資源)

能夠看出,二階段提交這麼作的就是讓前面都完成了準備工做,才能提交整個事務,若中間由某一環節出現問題,則整個事務回滾。

從兩階段提交的工做方式來看,很顯然,在提交事務的過程當中須要在多個節點之間進行協調,而各節點對鎖資源的釋放必須等到事務最終提交時,這樣,比起一階段提交,兩階段提交在執行一樣的事務時會消耗更多時間。事務執行時間的延長意味着鎖資源發生衝突的機率增長,當事務的併發量達到必定數量的時候,就會出現大量事務積壓甚至出現死鎖,系統性能就會嚴重下滑。

二階段提交看起來確實可以提供原子性的操做,可是不幸的事,二階段提交仍是有幾個缺點的:

一、同步阻塞問題。執行過程當中,全部參與節點都是事務阻塞型的。當參與者佔有公共資源時,其餘第三方節點訪問公共資源不得不處於阻塞狀態。

二、單點故障。因爲協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤爲在第二階段,協調者發生故障,那麼全部的參與者還都處於鎖定事務資源的狀態中,而沒法繼續完成事務操做。(若是是協調者掛掉,能夠從新選舉一個協調者,可是沒法解決由於協調者宕機致使的參與者處於阻塞狀態的問題)

三、數據不一致。在二階段提交的階段二中,當協調者向參與者發送commit請求以後,發生了局部網絡異常或者在發送commit請求過程當中協調者發生了故障,這回致使只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求以後就會執行commit操做。可是其餘部分未接到commit請求的機器則沒法執行事務提交。因而整個分佈式系統便出現了數據部一致性的現象。

四、二階段沒法解決的問題:協調者再發出commit消息以後宕機,而惟一接收到這條消息的參與者同時也宕機了。那麼即便協調者經過選舉協議產生了新的協調者,這條事務的狀態也是不肯定的,沒人知道事務是否被已經提交。

參考資料
http://www.infoq.com/cn/articles/xa-transactions-handle
http://blog.csdn.net/bluishglc/article/details/7612811
http://www.open-open.com/lib/view/open1429863503010.html
http://hedengcheng.com/?p=136
http://www.hollischuang.com/archives/681


http://www.javashuo.com/article/p-dujybzdo-kv.html原創文章,版權全部,轉載請註明出處

相關文章
相關標籤/搜索