注冊用戶即可下載全站資源 關注Java幫幫微信公眾號
 

mysql 對XA事務的支持

9
發表時間:2018-12-11 14:56

MySQL 從5.0.3開始支持XA分布式事務,且只有InnoDB存儲引擎支持。MySQL Connector/J 從5.0.0版本之后開始直接提供對XA的支持。

D61FA313-9E10-4FF4-9E6C-D364117F838A.png


   需要注意的是, 在DTP模型中,mysql屬于資源管理器(RM)。而一個完整的分布式事務中,一般會存在多個RM,由事務管理器TM來統一進行協調。因此,這里所說的mysql對XA分布式事務的支持,一般指的是單臺mysql實例如何執行自己的事務分支。

MySQL XA 事務SQL語法

  1. XA {START|BEGIN} xid [JOIN|RESUME]   //開啟XA事務,如果使用的是XA START而不是XA BEGIN,那么不支持[JOIN|RESUME],xid是一個唯一值,表示事務分支標識符

  2. XA END xid [SUSPEND [FOR MIGRATE]]   //結束一個XA事務,不支持[SUSPEND [FOR MIGRATE]]

  3. XA PREPARE xid 準備提交

  4. XA COMMIT xid [ONE PHASE] //提交,如果使用了ONE PHASE,則表示使用一階段提交。兩階段提交協議中,如果只有一個RM參與,那么可以優化為一階段提交

  5. XA ROLLBACK xid  //回滾

  6. XA RECOVER [CONVERT XID]  //列出所有處于PREPARE階段的XA事務

 下面是一個簡單的msyql XA事務案例,演示了mysql作為全局事務中的一個事務分支,將一行記錄插入到一個表中

  1. mysql> XA START 'xatest’;  //其中'xatest’就是xid的值

  2. Query OK, 0 rows affected (0.00 sec)

  3. mysql> insert into user(name) values("tianshozuhi");

  4. Query OK, 1 row affected (0.00 sec)

  5. mysql> XA END 'xatest';

  6. Query OK, 0 rows affected (0.00 sec)

  7. mysql> XA PREPARE 'xatest';

  8. Query OK, 0 rows affected (0.01 sec)

  9. mysql> XA COMMIT 'xatest';

  10. Query OK, 0 rows affected (0.01 sec)

Mysql XA事務狀態

日本韩国三级aⅴ在线观看XA事務的狀態,按照如下步驟進行展開

1.    使用XA START來啟動一個XA事務,并把它置于ACTIVE狀態。

2.    對于一個ACTIVE狀態的 XA事務,我們可以執行構成事務的SQL語句,然后發布一個XA END語句。XA END把事務放入IDLE狀態。

日本韩国三级aⅴ在线观看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列出,因為事務終止。

日本韩国三级aⅴ在线观看4.    對于一個PREPARED狀態的 XA事務,您可以發布一個XA COMMIT語句來提交和終止事務,或者發布XA ROLLBACK來回滾并終止事務。


日本韩国三级aⅴ在线观看    針對一個給定的客戶端連接而言,XA事務和非XA事務(即本地事務)是互斥的。例如,已經執行了”XA START”命令來開啟一個XA事務,則本地事務不會被啟動,直到XA事務已經被提交或被 回滾為止。相反的,如果已經使用START TRANSACTION啟動一個本地事務,則XA語句不能被使用,直到該事務被提交或被 回滾為止。

   最后,如果一個XA事務處于ACTIVE狀態,是不能直接進行提交的,如果這樣做,mysql會拋出異常:

  1. ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed

  2. when global transaction is in the ACTIVE state

3 關于XID的說明

   mysql中使用xid來作為一個事務分支的標識符。事實上xid作為事務分支標識符是在XA規范中定義的,在<< Distributed Transaction Processing: The XA Specification>> 4.2 節中,規定了一個xid的結構,通過C語言進行描述,如下:

  1. /?

  2. ? Transaction branch identification: XID and NULLXID:

  3. ?/

  4. #define XIDDATASIZE 128  /? size in bytes ?/

  5. #define MAXGTRIDSIZE 64  /? maximum size in bytes of gtrid ?/

  6. #define MAXBQUALSIZE 64  /? maximum size in bytes of bqual ?/

  7. struct xid_t {

  8.    long formatID;     /* format identifier */

  9.    long gtrid_length; /* value 1-64 */

  10.    long bqual_length; /* value 1-64 */

  11.    char data[XIDDATASIZE];

  12.    };

  13. /?

  14. ? A value of -1 in formatID means that the XID is null.

  15. ?/

  16. typedef struct xid_t XID;

  17. /?

  18. ? Declarations of routines by which RMs call TMs:

  19. ?/

  20. extern int ax_reg(int, XID ?, long);

  21. extern int ax_unreg(int, long);

日本韩国三级aⅴ在线观看XA規范定義了一個xid有4個部分組成:

gtrid:

   全局事務標識符(global transaction identifier),最大不能超過64字節

bqual:

日本韩国三级aⅴ在线观看    分支限定符(branch qualifier),最大不能超過64字節

data:

日本韩国三级aⅴ在线观看   xid的值,其是 gtrid和bqual拼接后的內容。因為gtrid和bqual最大都是64個字節,因此data的最大長度為128。不過,在xid的結構體中,并沒有gtrid和bqual,只有gtrid_length、bqual_length。由于二者的內容都存儲在data中,因此我們可以根據data反推出gtrid和bqual。舉例來說,假設gtrid為”g12345”(5個字節),bqual為”b456”(4個字節)。那么在構造xid結構體時,gtrid_length=5,bqual_length=4,data=”g12345b456”,那么在反推的時候:

從data[0]到data[gtrid_length-1]之間的部分就是gtrid的值;從data[gtrid_length]到data[gtrid_length+bqual_length-1]部分就是bqual的值。

formatId:

   而formatId的作用就是記錄gtrid、bqual的格式,類似于memcached中flags字段的作用。XA規范中通過一個結構體約定了xid的組成部分,但是并沒有規定data中存儲的gtrid、bqual內容到底應該是什么格式。你可以選擇使用數字,也可以選擇使用字符串,到底選擇什么由開發者自行決定,只要最終能保證data中的內容是全局唯一的即可。XA規范建議使用OSI CCR風格來組織xid的內容,此時formatId應該設置為0.


在mysql官方文檔中,關于xid的組成也有類似的說明:

  1. xid: gtrid [, bqual [, formatID ]]

其中,bqual、formatID是可選的。解釋如下:

gtrid : 是一個全局事務標識符(global transaction identifier),

日本韩国三级aⅴ在线观看bqual:是一個分支限定符(branch qualifier),如果沒有提供bqual,那么默認值為空字符串''。

formatID:是一個數字,用于標記gtrid和bqual值的格式,這是一個無符號整數(unsigned integer),也就是說,最小為0。如果沒有提供formatID,那么其默認值為1。

   

日本韩国三级aⅴ在线观看    特別需要注意的是,xid作為一個事務分支的標識符,理論上只要有分支限定符(bqual)就可以了,為什么要包含全局事務標識符(gtrid)?這主要是為了管理方便,通過包含進xid,我們可以很容易的判斷出這個事務分支屬于哪一個全局事務。

   例如,前面提到 XA RECOVER命令的作用是列出所有處于PREPARE階段的XA事務,以下是一個案例:

  1. mysql>  XA RECOVER;

  2. +----------+--------------+--------------+--------------+

  3. | formatID | gtrid_length | bqual_length | data         |

  4. +----------+--------------+--------------+--------------+

  5. |        1 |            6 |            6 | g12345b67890 |

  6. +----------+--------------+--------------+--------------+

日本韩国三级aⅴ在线观看這里列出的是一個分支事務xid的組成信息,根據前面的介紹,我們可以推斷出:

   gtrid是data[0]到data[gtrid_length-1]部分的內容,即data[0]到data[6-1=5]部分的內容,結果為g12345

   而bqual是data[gtrid_length]到data[gtrid_length+bqual_length-1]部分的內容,即data[6]到data[6+6-1=11]部分的內容,結果b67890

因此,根據這個信息,我們就可以判斷出這個xid表示的是:全局事務(g12345)中的事務分支(b67890)。

4、通過jdbc操作mysql xa事務

   MySQL Connector/J 從5.0.0版本之后開始直接提供對XA的支持,也就是提供了java版本XA接口的實現。意味著我們可以直接通過java代碼來執行mysql xa事務。

   需要注意的是,業務開發人員在編寫代碼時,不應該直接操作這些XA事務操作的接口。因為在DTP模型中,RM上的事務分支的開啟、結束、準備、提交、回滾等操作,都應該是由事務管理器TM來統一管理。

   由于目前我們還沒有接觸到TM,那么我們不妨做一回"人肉事務管理器",用你智慧的大腦,來控制多個mysql實例上xa事務分支的執行,提交/回滾。通過直接操作這些接口,你將對xa事務有更深刻的認識。

  1. import com.mysql.jdbc.jdbc2.optional.MysqlXAConnection;

  2. import com.mysql.jdbc.jdbc2.optional.MysqlXid;

  3. import javax.sql.XAConnection;

  4. import javax.transaction.xa.XAException;

  5. import javax.transaction.xa.XAResource;

  6. import javax.transaction.xa.Xid;

  7. import java.sql.Connection;

  8. import java.sql.DriverManager;

  9. import java.sql.PreparedStatement;

  10. import java.sql.SQLException;

  11. public class MysqlXAConnectionTest {

  12.   public static void main(String[] args) throws SQLException {

  13.      //true表示打印XA語句,,用于調試

  14.      boolean logXaCommands = true;

  15.      // 獲得資源管理器操作接口實例 RM1

  16.      Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "shxx12151022");

  17.      XAConnection xaConn1 = new MysqlXAConnection((com.mysql.jdbc.Connection) conn1, logXaCommands);

  18.      XAResource rm1 = xaConn1.getXAResource();

  19.      // 獲得資源管理器操作接口實例 RM2

  20.      Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",

  21.            "shxx12151022");

  22.      XAConnection xaConn2 = new MysqlXAConnection((com.mysql.jdbc.Connection) conn2, logXaCommands);

  23.      XAResource rm2 = xaConn2.getXAResource();

  24.      // AP請求TM執行一個分布式事務,TM生成全局事務id

  25.      byte[] gtrid = "g12345".getBytes();

  26.      int formatId = 1;

  27.      try {

  28.         // ==============分別執行RM1和RM2上的事務分支====================

  29.         // TM生成rm1上的事務分支id

  30.         byte[] bqual1 = "b00001".getBytes();

  31.         Xid xid1 = new MysqlXid(gtrid, bqual1, formatId);

  32.         // 執行rm1上的事務分支

  33.         rm1.start(xid1, XAResource.TMNOFLAGS);//One of TMNOFLAGS, TMJOIN, or TMRESUME.

  34.         PreparedStatement ps1 = conn1.prepareStatement("INSERT into user(name) VALUES ('tianshouzhi')");

  35.         ps1.execute();

  36.         rm1.end(xid1, XAResource.TMSUCCESS);

  37.         // TM生成rm2上的事務分支id

  38.         byte[] bqual2 = "b00002".getBytes();

  39.         Xid xid2 = new MysqlXid(gtrid, bqual2, formatId);

  40.         // 執行rm2上的事務分支

  41.         rm2.start(xid2, XAResource.TMNOFLAGS);

  42.         PreparedStatement ps2 = conn2.prepareStatement("INSERT into user(name) VALUES ('wangxiaoxiao')");

  43.         ps2.execute();

  44.         rm2.end(xid2, XAResource.TMSUCCESS);

  45.         // ===================兩階段提交================================

  46.         // phase1:詢問所有的RM 準備提交事務分支

  47.         int rm1_prepare = rm1.prepare(xid1);

  48.         int rm2_prepare = rm2.prepare(xid2);

  49.         // phase2:提交所有事務分支

  50.         boolean onePhase = false; //TM判斷有2個事務分支,所以不能優化為一階段提交

  51.         if (rm1_prepare == XAResource.XA_OK

  52.               && rm2_prepare == XAResource.XA_OK

  53.               ) {//所有事務分支都prepare成功,提交所有事務分支

  54.            rm1.commit(xid1, onePhase);

  55.            rm2.commit(xid2, onePhase);

  56.         } else {//如果有事務分支沒有成功,則回滾

  57.            rm1.rollback(xid1);

  58.            rm1.rollback(xid2);

  59.         }

  60.      } catch (XAException e) {

  61.         // 如果出現異常,也要進行回滾

  62.         e.printStackTrace();

  63.      }

  64.   }

  65. }

   在這個案例中,演示了2個RM的情況下分布式事務的工作流程。因為我們充當了"人肉事務管理器”TM,因此很多本應該由TM來處理的工作處理細節也直接體現在上述代碼中,如:生成全局事務id和分支事務id、在RM上開啟事務分支、兩階段提交等。雖然我們自己作為"人肉事務管理器”是很不可靠的,但是上述代碼可以讓我們了解一個TM內部的主要工作流程是怎樣的。

日本韩国三级aⅴ在线观看    在實際開發中,代碼絕不會像上表面那樣復雜,因為我們通常都會使用第三方或者容器提供的TM功能,因此在操作分布式事務時,代碼可以得到極大的簡化。

   最后,由于我們設置了logXaCommands=true,程序在運行的時候回打印出執行的XA命令。如下所示:

  1. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA START 0x673132333435,0x623030303031,0x1

  2. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA END 0x673132333435,0x623030303031,0x1

  3. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA START 0x673132333435,0x623030303032,0x1

  4. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA END 0x673132333435,0x623030303032,0x1

  5. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA PREPARE 0x673132333435,0x623030303031,0x1

  6. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA PREPARE 0x673132333435,0x623030303032,0x1

  7. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA COMMIT 0x673132333435,0x623030303031,0x1

  8. Fri Feb 02 18:09:29 CST 2018 DEBUG: Executing XA statement: XA COMMIT 0x673132333435,0x623030303032,0x1

5 MySQL Connector/J XA事務支持源碼簡單分析

   最后,我們對上述源碼進行一下簡單的分析。在前面直接使用mysql命令操作的時候,我們通過"XA START xid”等XA命令來執行XA事務。而在上述java代碼中,我們是獲取了一個普通的鏈接Connection之后,封裝成了MysqlXAConnection。如下:

com.mysql.jdbc.jdbc2.optional.MysqlXAConnection

  1. public class MysqlXAConnection extends MysqlPooledConnection implements XAConnection, XAResource {

  2.  private com.mysql.jdbc.Connection underlyingConnection;

  3.  private Log log;

  4.  protected boolean logXaCommands;

  5.  

  6.  //構造方法

  7.  public MysqlXAConnection(com.mysql.jdbc.Connection connection, boolean logXaCommands) throws SQLException {

  8.    super(connection);

  9.    this.underlyingConnection = connection;

  10.    this.log = connection.getLog();

  11.    this.logXaCommands = logXaCommands;

  12. }

  13. }

可以看到,MysqlXAConnection本身就實現了XAResource日本韩国三级aⅴ在线观看接口,因此當調用getXAResource()方法時,返回的就是其自己

com.mysql.jdbc.jdbc2.optional.MysqlXAConnection#getXAResource

  1. public XAResource getXAResource() throws SQLException {

  2.    return this;

  3. }

之后,我們調用XAResource的start方法來開啟XA事務。start方法源碼如下所示:

com.mysql.jdbc.jdbc2.optional.MysqlXAConnection#start

  1. public void start(Xid xid, int flags) throws XAException {

  2.    //1、封裝XA命令

  3.    StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH);

  4.    commandBuf.append("XA START ");

  5.    appendXid(commandBuf, xid);

  6.  

  7.    //2、添加flag標記

  8.    switch (flags) {

  9.        case TMJOIN:

  10.            commandBuf.append(" JOIN");

  11.            break;

  12.        case TMRESUME:

  13.            commandBuf.append(" RESUME");

  14.            break;

  15.        case TMNOFLAGS:

  16.            // no-op

  17.            break;

  18.        default:

  19.            throw new XAException(XAException.XAER_INVAL);

  20.    }

  21.    

  22.    //執行命令

  23.    dispatchCommand(commandBuf.toString());

  24.    this.underlyingConnection.setInGlobalTx(true);

  25. }

   可以看到,當我們調用MysqlXAConnection的start方法時,實際上就是執行了一個”XA START xid [JOIN|RESUME]”命令而已,和我們直接在命令行中的操作是一樣一樣的,只不過通過封裝簡化了我們的操作。

   對于MysqlXAConnection的end、prepare、commit、rollback等方法,也都是是類似的,不再贅述。

   最后提示, MySQL Connector/J 中提供的XA操作接口,如上面提到的XAConnection、XAResource、Xid等,實際上都遵循了JTA規范


上一篇JTA規范
文章分類: 分布式事物
分享到:
支付寶贊助-Java幫幫社區
微信贊助-Java幫幫社區
Java幫幫公眾號生態

Java幫幫公眾號生態

總有一款適合你

Java幫幫-微信公眾號

Java幫幫-微信公眾號

將分享做到極致

Python幫幫-公眾號

Python幫幫-公眾號

人工智能,爬蟲,學習教程

大數據驛站-微信公眾號

大數據驛站-微信公眾號

一起在數據中成長

九點編程-公眾號

九點編程-公眾號

深夜九點學編程

程序員生活志-公眾號

程序員生活志-公眾號

互聯網,職場,程序員那些事兒

Java幫幫學習群生態

Java幫幫學習群生態

總有一款能幫到你

Java學習群

Java學習群

與大牛一起交流

大數據學習群

大數據學習群

在數據中成長

九點編程學習群

九點編程學習群

深夜九點學編程

python學習群

python學習群

人工智能,爬蟲

測試學習群

測試學習群

感受測試的魅力

Java幫幫生態承諾

Java幫幫生態承諾

一直堅守,不負重望

初心
勤儉
誠信
正義
分享
合作品牌 非盈利生態-優質內容分享傳播者
關于我們
友鏈申請
友鏈交換:加幫主QQ2524138991 留言即可 24小時內答復  
全站內容非商業用途,內容來源于網友,并遵循 許可,如有異議請聯系客服。
會員登錄
獲取驗證碼
登錄
登錄
我的資料
留言
回到頂部