只会一点java

java持续学习者,每月一篇博客。罗列出技术栈,慢慢完善,持续学习,总有一天,你会追上甚至超越曾经的大神。
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

分布式事务(三)mysql对XA协议的支持

Posted on 2019-06-04 18:03  只会一点java  阅读(6494)  评论(0编辑  收藏  举报

系列目录

分布式事务(一)原理概览

分布式事务(二)JTA规范

分布式事务(三)mysql对XA协议的支持

分布式事务(四)简单样例

分布式事务(五)源码详解

分布式事务(六)总结提高

引子

从Mysql5开始,innoDB引擎支持XA协议的分布式事务。DTP模型中,一个TM(事务管理器管理)管理多个RM(资源管理器),每个RM维护自己的事务分支。在看源码之前我们看一下底层DB mysql对XA事务的支持。

1. XA语法

官网:13.3.8.1 XA Transaction SQL Syntax

 1 XA {START|BEGIN} xid [JOIN|RESUME]   开启XA事务,使用begin才能使用join/resume,start不支持
 2 
 3 XA END xid [SUSPEND [FOR MIGRATE]]   不支持SUSPEND [FOR MIGRATE]
 4 
 5 XA PREPARE xid                       二阶段提交的准备阶段
 6 
 7 XA COMMIT xid [ONE PHASE]            二阶段提交的提交阶段,ONE PHASE代表一阶段提交,如果只有一个rm参与者,那么二阶段提交优化为一阶段提交
 8 
 9 XA ROLLBACK xid                      回滚
10 
11 XA RECOVER [CONVERT XID]             列出所有处于prepared状态的事务

上面的语法中都有xid官方解释如下:

xid: gtrid [, bqual [, formatID ]]

其中,

gtrid:全局事务ID,不得超过64,建议使用十六进制数。

bqual:分支限定符(branch qualifier),如果没有提供bqual,那么默认值为空字符串'',长度不超过64,建议使用十六进制数。

formatID:是一个无符号整数,用于标记gtrid和bqual值的格式,默认为1,长度不超过64.

对应java接口:

 1 public interface Xid {
 2     int MAXGTRIDSIZE = 64;
 3     int MAXBQUALSIZE = 64;
 4 
 5     int getFormatId();
 6 
 7     byte[] getGlobalTransactionId();
 8 
 9     byte[] getBranchQualifier();
10 }

2. XA状态

官网:13.3.8.2 XA Transaction States

XA事务的状态,按照如下步骤进行展开

1.    使用XA START来启动一个XA事务,并把它置于ACTIVE状态。

2.    对于一个ACTIVE状态的 XA事务,我们可以执行构成事务的SQL语句,然后发布一个XA END语句。XA END使事务进入IDLE状态。

3.    对于一个IDLE 状态XA事务,可以执行一个XA PREPARE语句或一个XA COMMIT…ONE PHASE语句:

  • XA PREPARE把事务放入PREPARED状态。在此点上的XA RECOVER语句将在其输出中包括事务的xid值,因为XA RECOVER会列出处于PREPARED状态的所有XA事务。

  • XA COMMIT…ONE PHASE(优化成一阶段提交)用于预备和提交事务。xid值将不会被XA RECOVER列出,因为事务终止。

4.    对于一个PREPARED状态的 XA事务,执行XA COMMIT语句来提交和终止事务,或者执行XA ROLLBACK回滚并终止事务。 

 

 注意:

同一个客户端数据库连接,XA事务和非XA事务(即本地事务)是互斥的。例如,已经执行了”XA START”命令来开启一个XA事务,则本地事务不会被启动,直到XA事务已经被提交或被 回滚为止。相反的,如果已经使用START TRANSACTION启动一个本地事务,则XA语句不能被使用,直到该事务被提交或被 回滚为止。

3. 测试

 1 package study.xa;
 2 
 3 import com.mysql.jdbc.jdbc2.optional.MysqlXAConnection;
 4 import com.mysql.jdbc.jdbc2.optional.MysqlXid;
 5 
 6 import javax.sql.XAConnection;
 7 import javax.transaction.xa.XAException;
 8 import javax.transaction.xa.XAResource;
 9 import javax.transaction.xa.Xid;
10 import java.sql.Connection;
11 import java.sql.DriverManager;
12 import java.sql.PreparedStatement;
13 import java.sql.SQLException;
14 
15 /***
16  * @Description mysql分布式事务XAConnection模拟
17  * @author denny
18  * @date 2019/4/3 上午9:15
19  */
20 public class MysqlXaConnectionTest {
21 
22     public static void main(String[] args) throws SQLException {
23         //true表示打印XA语句,,用于调试
24         boolean logXaCommands = true;
25         // 获得资源管理器操作接口实例 RM1
26         Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "12345");
27         XAConnection xaConn1 = new MysqlXAConnection((com.mysql.jdbc.Connection)conn1, logXaCommands);
28         XAResource rm1 = xaConn1.getXAResource();
29 
30         // 获得资源管理器操作接口实例 RM2
31         Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test2", "root", "12345");
32         XAConnection xaConn2 = new MysqlXAConnection((com.mysql.jdbc.Connection)conn2, logXaCommands);
33         XAResource rm2 = xaConn2.getXAResource();
34         // AP请求TM执行一个分布式事务,TM生成全局事务id
35         byte[] gtrid = "g12345".getBytes();
36         int formatId = 1;
37         try {
38             // ==============分别执行RM1和RM2上的事务分支====================
39             // TM生成rm1上的事务分支id
40             byte[] bqual1 = "b00001".getBytes();
41             Xid xid1 = new MysqlXid(gtrid, bqual1, formatId);
42             // 执行rm1上的事务分支 One of TMNOFLAGS, TMJOIN, or TMRESUME.
43             rm1.start(xid1, XAResource.TMNOFLAGS);
44             // 业务1:插入user表
45             PreparedStatement ps1 = conn1.prepareStatement("INSERT into user VALUES ('99', 'user99')");
46             ps1.execute();
47             rm1.end(xid1, XAResource.TMSUCCESS);
48 
49             // TM生成rm2上的事务分支id
50             byte[] bqual2 = "b00002".getBytes();
51             Xid xid2 = new MysqlXid(gtrid, bqual2, formatId);
52             // 执行rm2上的事务分支
53             rm2.start(xid2, XAResource.TMNOFLAGS);
54             // 业务2:插入user_msg表
55             PreparedStatement ps2 = conn2.prepareStatement("INSERT into user_msg VALUES ('88', '99', 'user99的备注')");
56             ps2.execute();
57             rm2.end(xid2, XAResource.TMSUCCESS);
58 
59             // ===================两阶段提交================================
60             // phase1:询问所有的RM 准备提交事务分支
61             int rm1Prepare = rm1.prepare(xid1);
62             int rm2Prepare = rm2.prepare(xid2);
63             // phase2:提交所有事务分支
64             boolean onePhase = false;
65             //TM判断有2个事务分支,所以不能优化为一阶段提交
66             if (rm1Prepare == XAResource.XA_OK
67                 && rm2Prepare == XAResource.XA_OK
68                 ) {
69                 //所有事务分支都prepare成功,提交所有事务分支
70                 rm1.commit(xid1, onePhase);
71                 rm2.commit(xid2, onePhase);
72             } else {
73                 //如果有事务分支没有成功,则回滚
74                 rm1.rollback(xid1);
75                 rm1.rollback(xid2);
76             }
77         } catch (XAException e) {
78             // 如果出现异常,也要进行回滚
79             e.printStackTrace();
80         }
81     }
82 }

打印日志:

1 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA START 0x673132333435,0x623030303031,0x1
2 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA END 0x673132333435,0x623030303031,0x1
3 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA START 0x673132333435,0x623030303032,0x1
4 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA END 0x673132333435,0x623030303032,0x1
5 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA PREPARE 0x673132333435,0x623030303031,0x1
6 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA PREPARE 0x673132333435,0x623030303032,0x1
7 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA COMMIT 0x673132333435,0x623030303031,0x1
8 Tue Jun 04 17:08:18 CST 2019 DEBUG: Executing XA statement: XA COMMIT 0x673132333435,0x623030303032,0x1

 

 

=====参考======

http://www.tianshouzhi.com/api/tutorials/distributed_transaction/384