扯談下XA事務
普通事務
普通事務的實現是比較好理解的。以jdbm3為例,大概是這樣的過程:
每個事務都新建一個事務文件,當commit時,先把修改過的數據塊,寫到事務文件裏,然後再一次性地寫到數據庫文件裏。
如果commit時掛掉了,那麼重啟之後,會再次從事務文件裏把修改過的塊寫到數據庫文件裏。最後再刪除事務文件。
https://github.com/jankotek/JDBM3
但是XA事務,即所謂的分布式事務卻令人感到雲裏霧裏。一是資料很少,網上的各種配置資料都是流於表麵;二是可能實際應用的人也少。
最近研究了下,算是找到點門道了。
二階段提交(Two-phase Commit)
首先,XA事務是基於二階段提交(Two-phase Commit)實現的。二階段提交本身並沒有什麼令人疑惑的地方。看wiki就可以知道是怎麼回事了。
簡而言之,有二種角色,事務管理者(DM, Transaction Manager),資源管理器(RM, Resource Manager),通常即數據庫或者JMS服務器。
下麵兩個圖片來自:https://www.infoq.com/cn/articles/xa-transactions-handle
出錯回滾:
當然,還有各種中間出錯時,要處理的情況,詳細可以看infoq的原文。
令人疑惑的atomikos
二階段提交協議是很容易理解的,但是真正令我疑惑的是Java實現的atomikos,一個分布式事務的Transaction Manager組件。
開始的時候,我以為事務管理器(TM)都是獨立的一個服務,或者一個獨立的進程,它和資源管理器(RM)之間通過網絡通迅。
但是在網上看一些atomikos配置文章,都沒有提到如何配置一個獨立的Transaction Manager,隻是簡單地介紹了下如何配置atomikos,這些配置都是和應用在一起的。
而從配置裏麵也沒法看出是如何保證在事務過程中,如果應用的進程掛掉後,是如何恢複的。
再把atomikos的例子代碼下載下來,發現也沒有提到是如何保證事務在失敗後,如何協調的。
比如,在第二段提交時,當RM1 commit完成了,而RM2 commit還沒有完成,而這時TM,即配置了atomikos的應用程序崩潰,那麼這個事務並沒有完成,還需要TM重啟後協調,才能最終完成這個事務。但是沒看到恢複部分的配置。
沒辦法,隻能親自跑一遍代碼了。
跑了下atomikos的代碼,在第二階段提交時,把進程殺掉,發現的確是可以自動處理回滾事務,或者再次提交的。那麼信息是保存在哪裏的?也沒有看到有什麼配置文件。
最終,隻能下XA的規範下載下來,再一點點慢慢看。
在The XA Specification裏的2.3小節:Transaction Completion and Recovery 明確提到TM是要記錄日誌的:
In Phase 2, the TM issues all RMs an actual request to commit or roll back the
transaction branch, as the case may be. (Before issuing requests to commit, the TM
stably records the fact that it decided to commit, as well as a list of all involved RMs.)
All RMs commit or roll back changes to shared resources and then return status to the
TM. The TM can then discard its knowledge of the global transaction.
TM是一定要把事務的信息,比如XID,哪個RM已經完成了等保存起來的。隻有當全部的RM提交或者回滾完後,才能丟棄這些事務的信息。
於是再查看下atomikos例子運行目錄,果然有一些文件日誌文件:
127.0.1.1.tm13.epoch
tmlog13.log
tmlog.lck
tm.out
tm.out.lck
原來atomikos是通過在應用的目錄下生成日誌文件來保證,如果失敗,在重啟後可以通過日誌來完成未完成的事務。
XA事務的假設條件
從XA的規範裏找到了下麵的說法:
The X/Open DTP model makes these assumptions:
TMs and RMs have access to stable storage TM和RM都有牢靠的存儲
TMs coordinate and control recovery TM協調和控製恢複流程
RMs provide for their own restart and recovery of their own state. On request, an RM must give a TM a list of XIDs that the RM has prepared for commitment or has heuristically completed. RM在得啟和恢複時,得回應TM的請求,返回一係列的XID,是prepared的,或者是已經啟發式地完成了的
也就是說,XA事務都假定了TM和RM都是有牢靠的存儲的,所以也保證了TM重啟後可以從日誌裏恢複還沒處理完的事務。
TM可以向RM查詢事務的狀態,RM必須要返回一係列事務的XID,表明事務是prepared狀態,還是已經commit的狀態。
到這裏,應該很明了了,XA事務是其限製的,而TM是XA事務的一個單點,TM必須要非常地牢靠。
從XA的接口函數,就可以大概看出協議是怎麼工作的(來自XA規範文檔):
如何避免XA事務
XA事務的明顯問題是timeout問題,比如當一個RM出問題了,那麼整個事務隻能處於等待狀態。這樣可以會連鎖反應,導致整個係統都很慢,最終不可用。
避免使用XA事務的方法通常是最終一致性。
舉個例子,比如用戶充值300元,為了減少DB的壓力,先把這個放到消息隊列裏,然後後端再從消息隊列裏取出消息,更新DB。
那麼如何保證,這條消息不會被重複消費?或者重複消費後,仍能保證結果是正確的?
- 在消息裏帶上用戶帳號在數據庫裏的版本,在更新時比較數據的版本,如果相同則加上300;
- 比如用戶本來有500元,那麼消息是更新用戶的錢數為800,而不是加上300;
- 另外建一個消息是否被消費的表,記錄消息ID,在事務裏,先判斷消息是否已經消息過,如果沒有,則更新數據庫,加上300,否則說明已經消費過了,丟棄。
前麵兩種方法都必須從流程上保證是單方向的,不能插入其它的東東。
其它的一些東東:
貌似一直有人想用zookeeper來實現2pc,或者類似的東東,因為zookeeper是比較可靠的。但是感覺也沒有辦法解決timeout問題。
微軟的XA事務恢複流程的文檔:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms681775(v=vs.85).aspx
There are two forms of XA transaction recovery, as follows:
- Cold recovery. Cold recovery performed if the transaction manager process fails while a connection to an XA resource manager is open. When the transaction manager restarts, it reads the transaction manager log file and re-establishes the connection to the XA resource manager by calling xa_open_entry. It then initiates XA recover by calling xa_recover_entry.
- Hot recovery. Hot recovery is performed if the transaction manager remains up while the connection between the transaction manager and the XA resource manager fails because the XA resource manager or the network fails. After the failure, the transaction manager periodically calls xa_open_entry to reconnect to the XA resource manager. When the connection is reestablished, the transaction manager initiates XA recovery by calling xa_recover_entry.
總結:
XA事務沒有什麼神秘的地方,二階段提交也是一個人們很自然的一個處理方式。
隻不過,這個是規範,如果有多個資源之間要協調,而且都支持XA事務,那麼會比較方便 。
參考:
The XA Specification 可以從這裏下載到:https://download.csdn.net/detail/hengyunabc/6940529
https://en.wikipedia.org/wiki/Two-phase_commit_protocol
https://www.infoq.com/cn/articles/xa-transactions-handle
https://java.sun.com/javaee/technologies/jta/index.jsp
https://github.com/bitronix/btm 一個開源的JTA Transaction Manager
最後更新:2017-04-03 12:55:09
上一篇:
openstack 命令行管理九 - flavor管理[主機模板] (備忘)
下一篇:
slidingmenu開源效果
Android之ExpandableListView下拉分組的實現
Activity完全加載完畢後的回調函數
[轉貼]提升進程權限為debug權限,無法禁止進程
『0015』 - Solidity Types - 動態字節數組(Dynamically-sized byte array)、固定大小字節數組(Fixed-size byte arrays)、string之間的轉換關係
哪裏有大量的H5響應式模板下載
java java.util.ConcurrentModificationException 原因以及解決方案
PostgreSQL 忘記提交2PC事務對數據庫造成的危害.
Android為Notification加上一個進度條
阿裏雲最便宜的服務器多少錢?199一年!
為什麼說產品化是私有IaaS的唯一出路?