您好,登錄后才能下訂單哦!
在具體的 Demo 之前,先來補充一點 XA 事務的知識: DTP 模型與 XA 規范。
DTP 模型與 XA 規范是由 X/Open 維護,也就是現在的 open group,官方網址:http://www.opengroup.org/。open group 是一個獨立的組織,主要負責制定各種行業技術標準。由各大知名公司或者廠商進行支持,主要有如下公司:
open group 目前有八家公司,華為就是其中的一家。在分布式事務處理(Distributed Transaction Processing,簡稱DTP)方面,X/Open主要提供了以下參考文檔:
DTP 模型
在《Distributed Transaction Processing: Reference Model 》 第3版中,規定了構成 DTP 模型的 5個基本元素:
DTP 模型元素更深層次的東西可以參考 opengroup 的文檔,接下來聊一聊 DTP 實例,一個 DTP 實例至少包含 AP、RMs、TM 三部分。如下圖所示:
我們可以看出 AP、RMs、TM 三者之間都是有交互的,大概流程如下:
那什么是 XA 協議呢?XA 規范是定義交互接口,從上面的圖中可以看出,整個 DTP 中,有三個交互接口,XA 規范主要是 TM 和 RMs 之間。下面這張圖好理解一些:
好了,關于 DTP 模型與 XA 規范就聊這么多,具體的可以查看 opengroup 提供的文檔,下面就用我們熟悉的 MySQL 數據庫來實現一個 XA 事務協議第二階段提交。
MySQL 從5.0.3開始支持XA分布式事務,且只有InnoDB存儲引擎支持。如下圖:
其他的我就不說了,這里我提一下 XA 事務狀態,一個完整的事務流程如下:
總結一下,XA 事務, 通過 Start 啟動一個 XA 事務,并且被置為 Active 狀態,處在 active 狀態的事務可以執行 SQL 語句,通過 END 方法將 XA 事務置為 IDLE 狀態。處于 IDLE 狀態可以執行 PREPARE 操作或者 COMMIT…ONE PHASE 操作,也就是二階段提交中的第一階段,PREPARED 狀態的 XA事務的時候就可以 Commit 或者 RollBack,也就是二階段提交的第二階段。
可能你注意到了上面有一個 XID 值,簡單的講一下,MySQL 中使用xid來作為一個事務分支的標識符。關于 xid 在 XA 規范中有定義,XA規范定義了一個xid由4個部分組成:
好了,關于 XA 事務就 BB 這么多了,接下來,我們通過一個實例,來實現一把基于 XA 事務協議第二階段提交。
場景: 模擬現金 + 紅包組合支付,假設我們購買了 100 塊錢的東西,90塊使用現金支付,10 塊紅包支付,現金和紅包處在不同的庫。
假設: 現在有兩個庫:xa_account(賬戶庫,現金庫)、xa_red_account(紅包庫)。兩個庫下面都有一張 account 表,account 表中的字段也比較簡單,就 id、user_id、balance_amount 三個字段,SQL 我就不貼了。
好了,具體代碼如下:
public class XaDemo {
public static void main(String[] args) throws Exception{
// 是否開啟日志
boolean logXaCommands = true;
// 獲取賬戶庫的 rm(ap做的事情)
Connection accountConn = DriverManager.getConnection("jdbc:mysql://106.12.12.xxxx:3306/xa_account?useUnicode=true&characterEncoding=utf8","root","xxxxx");
XAConnection accConn = new MysqlXAConnection((JdbcConnection) accountConn, logXaCommands);
XAResource accountRm = accConn.getXAResource();
// 獲取紅包庫的RM
Connection redConn = DriverManager.getConnection("jdbc:mysql://106.12.12.xxxx:3306/xa_red_account?useUnicode=true&characterEncoding=utf8","root","xxxxxx");
XAConnection Conn2 = new MysqlXAConnection((JdbcConnection) redConn, logXaCommands);
XAResource redRm = Conn2.getXAResource();
// XA 事務開始了
// 全局事務
byte[] globalId = UUID.randomUUID().toString().getBytes();
// 就一個標識
int formatId = 1;
// 賬戶的分支事務
byte[] accBqual = UUID.randomUUID().toString().getBytes();;
Xid xid = new MysqlXid(globalId, accBqual, formatId);
// 紅包分支事務
byte[] redBqual = UUID.randomUUID().toString().getBytes();;
Xid xid1 = new MysqlXid(globalId, redBqual, formatId);
try {
// 賬號事務開始 此時狀態:ACTIVE
accountRm.start(xid, XAResource.TMNOFLAGS);
// 模擬業務
String sql = "update account set balance_amount=balance_amount-90 where user_id=1";
PreparedStatement ps1 = accountConn.prepareStatement(sql);
ps1.execute();
accountRm.end(xid, XAResource.TMSUCCESS);
// 賬號 XA 事務 此時狀態:IDLE
// 紅包分支事務開始
redRm.start(xid1, XAResource.TMNOFLAGS);
// 模擬業務
String sql1 = "update account set balance_amount=balance_amount-10 where user_id=1";
PreparedStatement ps2 = redConn.prepareStatement(sql1);
ps2.execute();
redRm.end(xid1, XAResource.TMSUCCESS);
// 第一階段:準備提交
int rm1_prepare = accountRm.prepare(xid);
int rm2_prepare = redRm.prepare(xid1);
// XA 事務 此時狀態:PREPARED
// 第二階段:TM 根據第一階段的情況決定是提交還是回滾
boolean //TM判斷有2個事務分支,所以不能優化為一階段提交
if (rm1_prepare == XAResource.XA_OK && rm2_prepare == XAResource.XA_OK) {
accountRm.commit(xid, onePhase);
redRm.commit(xid1, onePhase);
} else {
accountRm.rollback(xid);
redRm.rollback(xid1);
}
} catch (Exception e) {
// 出現異常,回滾
accountRm.rollback(xid);
redRm.rollback(xid1);
e.printStackTrace();
}
}
}
運行程序,可以看到如下結果:
從圖中可以清楚看出 XA 事務兩階段提交過程,更多細節請查閱 MySQL 數據庫 XA Transactions 模塊。
今天的分享就這些,希望這篇文章對你的學習或者工作有所幫助.
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。