Distributed Transaction [2] mysql support for XA transactions

2.0 mysql support for XA transactions

 2018-02-05 02:20:50  4,994  1

 MySQL 5.0.3 from the beginning to support XA distributed transactions, and only InnoDB storage engine support. MySQL Connector / J 5.0.0 version from after the start of direct support for XA.

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

 

    It should be noted that, in the DTP model, mysql belong to the resource manager (RM). And a complete distributed transaction, usually there are multiple RM, to unify coordinated by a transaction manager TM. So, here's mysql support for XA distributed transactions, generally refers to how a single instance of mysql execute their own affairs branch.

MySQL XA Transaction SQL Syntax

https://dev.mysql.com/doc/refman/5.7/en/xa-statements.html

 
  1. XA {START | BEGIN} xid [JOIN | RESUME] // open XA transaction, if you are using XA START instead of XA BEGIN, it does not support [JOIN | RESUME], xid is a unique value representing the transaction branch identifier
  2. XA END xid [SUSPEND [FOR MIGRATE]] // end an XA transaction, does not support the [SUSPEND [FOR MIGRATE]]
  3. XA PREPARE xid ready to submit
  4. XA COMMIT xid [ONE PHASE] // submit, if you use ONE PHASE, it means using one-phase commit. Two-phase commit protocol, if only one RM involved, it can be optimized for one-phase commit
  5. XA ROLLBACK xid // rollback
  6. XA RECOVER [CONVERT XID] // lists all XA transactions in the PREPARE phase

  Here is a simple case of msyql XA transaction, the transaction demonstrates mysql as a branch of the global transaction, a row is inserted into a table

 
  1. mysql> XA START 'xatest'; // where 'xatest' is the value of xid
  2. Query OK, 0 rows affected (0.00 sec)
  3.  
  4. mysql> insert into user(name) values("tianshozuhi");
  5. Query OK, 1 row affected (0.00 sec)
  6.  
  7. mysql> XA END 'xatest';
  8. Query OK, 0 rows affected (0.00 sec)
  9.  
  10. mysql> XA PREPARE 'xatest';
  11. Query OK, 0 rows affected (0.01 sec)
  12.  
  13. mysql> XA COMMIT 'xatest';
  14. Query OK, 0 rows affected (0.01 sec)

Mysql XA transaction state

https://dev.mysql.com/doc/refman/5.7/en/xa-states.html

XA transaction state, the following steps are expanded

1. Use XA START to start an XA transaction and put it in ACTIVEthe state.

2. For a ACTIVE XA transaction state, we can execute SQL statements constitute matters, then publish a XA END statement. XA END puts the transaction in IDLEthe state.

3. For a state IDLE XA transaction, you can perform an XA PREPARE statement or an XA COMMIT ... ONE PHASE statement:

  • XA PREPARE puts the transaction in PREPAREDthe state. XA RECOVER statement at this point will include the transaction's xid value in its output, because XA RECOVER lists all XA transactions in the PREPARED state.

  • XA COMMIT ... ONE PHASE for the preparation and submission of the transaction. xid value will not be listed XA RECOVER, because the transaction is terminated.

4. For a PREPARED XA transaction state, you can publish a XA COMMIT statement to commit and terminate the transaction, or release XA ROLLBACK to roll back and terminate the transaction. 

 

    For a given client connection concerned, XA transactions and non-XA transaction (that is, local transactions) are mutually exclusive. For example, it has been carried out "XA START" command to open an XA transaction, local transaction can not be started until the XA transaction has been committed or rolled back. Conversely, if you have a local transaction started using START TRANSACTION, the XA statement can not be used until the transaction is committed or rolled back.

    Finally, if an XA transaction in the ACTIVE state, can not be submitted directly, if you do, mysql will throw an exception: 

 
  1. ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed
  2. when global transaction is in the ACTIVE state

3 description of the XID

    mysql xid used as an identifier of a transaction branch. Indeed xid transaction branch identifier is as defined in the XA specification, the << Distributed Transaction Processing: The XA Specification >> section 4.2, xid a predetermined structure, will be described by C language, as follows: 

 
  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);

XA specification defines a xid has four components:

gtrid:

    A global transaction identifier (global transaction identifier), can not exceed a maximum of 64 bytes

BQUAL:

    Branch qualifier (branch qualifier), can not exceed a maximum of 64 bytes

data:

   Xid value, which is the contents bqual gtrid and splicing. Because bqual gtrid and maximum are 64 bytes, the maximum length of data 128. However, in the structure of xid, there is no gtrid and bqual, only gtrid_length, bqual_length. Since the contents of both are stored in the data, so we can launch anti-gtrid and bqual according to data. For example, if gtrid as "g12345" (5 bytes), the bqual as "b456" (4 bytes). Xid then constructing the structure, gtrid_length = 5, bqual_length = 4, data = "g12345b456", then in reverse thrust when:

From the data [0] is the value of the data between gtrid [gtrid_length-1] section; from the data [gtrid_length] to data [gtrid_length bqual_length-1 +] is the value part of bqual.

formatId:

    And the recording function is to formatId gtrid, bqual format, similar to the role of the flags field in memcached. XA Specification by a part of the convention structure xid, but does not require gtrid data stored, the bqual what format the content should be in the end. You can use the numbers, you can also choose to use a string, in the end choose what the discretion of the developer, as long as the ultimate guarantee of content data can be globally unique. XA specification recommends the use of OSI CCR style to organize content xid, in which case formatId should be set to 0. 

 

In mysql official documentation, the composition of the xid has a similar explanation:

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

Which, bqual, formatID is optional. Explained as follows:

gtrid: is a global transaction identifier (global transaction identifier),

bqual: qualifier is a branch (branch qualifier), if no bqual, then the default is an empty string ''.

formatID: is a number, and a format for marking gtrid bqual value, which is an unsigned integer (unsigned integer), that is, the minimum is 0. If no formatID, then its default value is 1.

    

    Of particular note is that, xid as a transaction branch identifier, in theory, as long as the branch qualifier (bqual) on it, why should contain global transaction identifier (gtrid)? This is mainly for the convenience of management, by including into the xid, we can easily determine which global transaction belongs to a branch of the transaction. 

    For example, the aforementioned XA RECOVER command role is to list all the XA transaction is in the PREPARE phase, the following is a case:

 
  1. mysql>  XA RECOVER;
  2. +----------+--------------+--------------+--------------+
  3. | formatID | gtrid_length | bqual_length | data         |
  4. +----------+--------------+--------------+--------------+
  5. |        1 |            6 |            6 | g12345b67890 |
  6. +----------+--------------+--------------+--------------+

The information listed here is composed of a branch of the transaction xid, according to previous reports, we can infer that:

    gtrid data is [0] content data [gtrid_length-1] of the section, i.e., data [0] to the data content [6-1 = 5] section, the result is g12345;

    Bqual is the data [gtrid_length] to data [gtrid_length + bqual_length-1] of the content portion, i.e., data [6] to the content data [6 + 6-1 = 11] portion, results b67890.

Therefore, based on this information, we can determine the xid is represented: global transaction (g12345) in the transaction branch (b67890).

4, operating through jdbc mysql xa Affairs

    MySQL Connector / J from a later version 5.0.0 began offering direct support for XA, which is provided to achieve java version of the XA interface. Means that we can directly execute mysql xa transaction java code.

    It should be noted that the business development staff in the preparation of the code, should not directly manipulate these interfaces XA transaction operations. Because DTP model, RM transaction branches on the open end, prepare, commit, rollback and other operations, should be by the Transaction Manager (TM) unified management.

    Since we have not yet come into contact with TM, then we might make a return to the "human flesh Transaction Manager", with the wisdom of your brain, to control multiple instances on the implementation of mysql transaction branch xa, commit / rollback. By directly manipulating these interfaces, you will xa Affairs deeper understanding.

 
  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 indicates that the print ,, XA statements for debugging
  14.       boolean logXaCommands = true;
  15.       // Get the operation of the resource management interface instance 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.       // Get the operation of the resource management interface instance 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 perform a distributed transaction request TM, TM generates a global transaction id
  25.       byte[] gtrid = "g12345".getBytes();
  26.       int formatId = 1;
  27.       try {
  28.          // Run the transaction branch ==================== ============== on the RM1 and RM2
  29.          Id on the transaction branch // TM generated rm1
  30.          byte[] bqual1 = "b00001".getBytes();
  31.          Xid xid1 = new MysqlXid(gtrid, bqual1, formatId);
  32.          // execute the transaction branch on 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 generates a transaction branch id on rm2
  38.          byte[] bqual2 = "b00002".getBytes();
  39.          Xid xid2 = new MysqlXid(gtrid, bqual2, formatId);
  40.          // execute the transaction branch on 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.          // =================== two-phase commit ========================== ======
  46.          // phase1: RM ask all ready to commit the transaction branch
  47.          int rm1_prepare = rm1.prepare(xid1);
  48.          int rm2_prepare = rm2.prepare(xid2);
  49.          // phase2: commit all transaction branches
  50.          boolean onePhase = false; // TM Analyzing transaction has two branches, the optimization can not be submitted to a stage
  51.          if (rm1_prepare == XAResource.XA_OK
  52.                && rm2_prepare == XAResource.XA_OK
  53.                ) {// prepare all transactions are successful branch, commit all transaction branch
  54.             rm1.commit(xid1, onePhase);
  55.             rm2.commit(xid2, onePhase);
  56.          } Else {// If there is no successful transaction branch, the rollback
  57.             rm1.rollback(xid1);
  58.             rm1.rollback(xid2);
  59.          }
  60.       } catch (XAException e) {
  61.          // if an exception occurs, but also rolled back
  62.          e.printStackTrace ();
  63.       }
  64.    }
  65. }

    In this case, we demonstrate workflow distributed transaction in the case of two RM. Because we act as "human affairs manager" TM, so a lot of this should work deal with the details of TM handled directly reflected in the above code, such as: generating global transaction id and branch transaction id, open transaction branch on the RM, two-phase commit and so on. While we ourselves, as "human affairs manager" is very reliable, but the above code allows us to understand a TM major internal workflow is like.

    In the actual development, the code will not be as complex as on the surface, because we usually use a third party or containers provided by TM function, so when operating a distributed transaction, the code can be greatly simplified.

    Finally, since we are logXaCommands = true, when the program is running back to print out the XA command execution. As follows: 

 
  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 transaction support simple source code analysis

    Finally, we have the source code for the above to make a simple analysis. When using the mysql command directly in front of the operation, we performed XA transaction "XA START xid" etc. XA command. After java in the above code, we get a common link Connection, packaging has become MysqlXAConnection. as follows:

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.   //Construction method
  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. }

You can see, MysqlXAConnection itself implements XAResourcethe interface, so when we getXAResource () method, is the return of its own

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

 
  1. public XAResource getXAResource() throws SQLException {
  2.     return this;
  3. }

After that, we start method is called to open XAResource XA transaction. Source start method is as follows:

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

 
  1. public void start(Xid xid, int flags) throws XAException {
  2.     // 1, the package XA command
  3.     StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH);
  4.     commandBuf.append("XA START ");
  5.     appendXid(commandBuf, xid);
  6.    
  7.     // 2, add the flag marker
  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.             // now-on
  17.             break;
  18.         default:
  19.             throw new XAException(XAException.XAER_INVAL);
  20.     }
  21.     
  22.     //Excuting an order
  23.     dispatchCommand(commandBuf.toString());
  24.     this.underlyingConnection.setInGlobalTx(true);
  25. }

    We can see that when we call MysqlXAConnection start method is actually implemented a "XA START xid [JOIN | RESUME]" command it, and we directly from the command line is the same as the same, but by encapsulating simplify our operations.

    For the end, prepare, commit, rollback MysqlXAConnection the like, are also similar, omitted.

    A final note, MySQL Connector / J XA provided user interface, an XAConnection as mentioned above, XAResource, Xid the like, followed JTA specification in fact, on the JTA specification will describe in the following section. 

Published 19 original articles · won praise 149 · views 800 000 +

Guess you like

Origin blog.csdn.net/truelove12358/article/details/100541455