zoukankan      html  css  js  c++  java
  • XA 分布式事务原理(转)

    XA是由X/Open组织提出的分布式事务的规范。 XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口。主流的关系型 数据库产品都是实现了XA接口的。
     XA接口是双向的系统接口,在事务管理器 (TM)以及一个或多个资源管理器(RM)之 间形成通信桥梁。
     XA之所以需要引入事务管理器是因为,在分布 式系统中,从理论上讲两台机器理论上无法达 到一致的状态,需要引入一个单点进行协调。
     由全局事务管理器管理和协调的事务,可以跨 越多个资源(如数据库或JMS队列)和进程。 全局事务管理器一般使用 XA 二阶段提交协议 与数据库进行交互。

           资源管理器(resource manager):用来管理系统资源,是通向事务资源的途径。数据库就是一种资源管理器。资源管理还应该具有管理事务提交或回滚的能力。
    事务管理器(transaction manager):事务管理器是分布式事务的核心管理者。事务管理器与每个资源管理器(resource manager)进行通信,协调并完成事务的处理。事务的各个分支由唯一命名进行标识
    Xid 接口 Xid, Xid 接口是 X/Open 事务标识符 XID 结构的 Java 映射。此接口指定三个访问器方法,以检索全局事务格式 ID、全局事务 ID 和分支限定符。Xid 接口供事务管理器和资源管理器使用。此接口对应用程序不可见。
    XA 不能自动提交。

    分段提交
    XA需要两阶段提交: prepare 和 commit. 
    第一阶段为 准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。 
    第二阶段为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。 

    假设有两个Connection, con1, con2, 大体的过程如下 .

    con1 = XAResouce1.getConnection...     
    con2 = XAResouce2.getConnection...   
     
    con1 do some thing.     
    con2 do some thing.     
    after they finish.     
     
    pre1 = XAResouce1.prepare();     
    pre2 = XAResouce2.prepare();     
     
    if( both pre1 and pre2 are OK){     
    XAResouce1 and 2 commit     
    }else {     
    XAResouce1 and 2 rollback     
    }    

    事务协调/管理者
    因为XA 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。MySQL 在这个XA事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。


    测试用例

    import com.alibaba.druid.pool.xa.DruidXADataSource;
    import com.mysql.jdbc.jdbc2.optional.MysqlXid;
     
    import javax.sql.XAConnection;
    import javax.transaction.xa.XAException;
    import javax.transaction.xa.XAResource;
    import javax.transaction.xa.Xid;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
     
     
    class DistributeTransaction {
     
        private Properties props;
        private String propertyfile = "jdbc.properties";
     
        private String sql_1 = "delete from test3 where pk_t=3;";
        private String sql_2 = "INSERT INTO test(name) VALUES('tyz');";
     
        DistributeTransaction() {
            Connection connection_1 = null;
            Connection connection_2 = null;
            DruidXADataSource xaDataSource_1 = null;
            DruidXADataSource xaDataSource_2 = null;
            Xid xid_1 = null;
            Xid xid_2 = null;
            XAConnection xaConnection_1 = null;
            XAConnection xaConnection_2 = null;
            XAResource xaResource_1 = null;
            XAResource xaResource_2 = null;
     
            try {
                props = new Properties();
                props.load(getClass().getResourceAsStream(propertyfile));
            } catch (IOException io) {
                System.err.println("Error while accessing the properties file (" + propertyfile + "). Abort.");
                System.exit(1);
            }
     
            DruidXADataSource[] xaDataSources = initXADataSource();
            xaDataSource_1 = xaDataSources[0];
            xaDataSource_2 = xaDataSources[1];
     
            XAConnection[] xaConnections = initXAConnection(xaDataSource_1, xaDataSource_2);
            xaConnection_1 = xaConnections[0];
            xaConnection_2 = xaConnections[1];
     
            xaResource_1 = initXAResource(xaConnection_1);
            xaResource_2 = initXAResource(xaConnection_2);
     
            connection_1 = getDatabaseConnection(xaConnection_1);
            connection_2 = getDatabaseConnection(xaConnection_2);
     
            // create a separate branch for a common transaction
            Xid[] xids = createXID();
            xid_1 = xids[0];
            xid_2 = xids[1];
     
            try {
                execBranch(connection_1, xaResource_1, xid_1, sql_1);
                execBranch(connection_2, xaResource_2, xid_2, sql_2);
     
                if (prepareCommit(xaResource_1, xid_1) == XAResource.XA_OK &&
                        prepareCommit(xaResource_2, xid_2) == XAResource.XA_OK) {
                    commitBranch(xaResource_1, xid_1);
                    commitBranch(xaResource_2, xid_2);
                } else {
                    throw new RuntimeException();
                }
            } catch (Exception e) {
                rollbackBranch(xaResource_1, xid_1);
                rollbackBranch(xaResource_2, xid_2);
            }
        }
     
        DruidXADataSource[] initXADataSource() {
            System.out.print("Create a XADataSource_1 data source: ");
            DruidXADataSource xaDataSource_1 = new DruidXADataSource();
            xaDataSource_1.setDbType(props.getProperty("db1.dbtype"));
            xaDataSource_1.setUrl(props.getProperty("db1.url"));
            xaDataSource_1.setUsername(props.getProperty("db1.username"));
            xaDataSource_1.setPassword(props.getProperty("db1.password"));
            System.out.println("Okay.");
     
            System.out.print("Create a XADataSource_2 data source: ");
            DruidXADataSource xaDataSource_2 = new DruidXADataSource();
            xaDataSource_2.setDbType(props.getProperty("db2.dbtype"));
            xaDataSource_2.setUrl(props.getProperty("db2.url"));
            xaDataSource_2.setUsername(props.getProperty("db2.username"));
            xaDataSource_2.setPassword(props.getProperty("db2.password"));
            System.out.println("Okay.");
            return new DruidXADataSource[]{xaDataSource_1, xaDataSource_2};
        }
     
        XAConnection[] initXAConnection(DruidXADataSource xaDataSource_1, DruidXADataSource xaDataSource_2) {
            XAConnection xaconn_1 = null;
            XAConnection xaconn_2 = null;
            try {
                System.out.print("Set up DB_1 XA connection: ");
                xaconn_1 = xaDataSource_1.getXAConnection();
                System.out.println("Okay.");
     
                System.out.print("Set up DB_2 XA connection: ");
                xaconn_2 = xaDataSource_2.getXAConnection();
                System.out.println("Okay.");
            } catch (SQLException e) {
                sqlerr(e);
            }
            return new XAConnection[]{xaconn_1, xaconn_2};
        }
     
        XAResource initXAResource(XAConnection xacon) {
            XAResource xares = null;
            try {
                System.out.print("Setting up a XA resource: ");
                xares = xacon.getXAResource();
                System.out.println("Okay.");
            } catch (SQLException e) {
                sqlerr(e);
            }
            return xares;
        }
     
        Connection getDatabaseConnection(XAConnection xacon) {
            Connection con = null;
            try {
                System.out.print("Establish database connection: ");
                con = xacon.getConnection();
                con.setAutoCommit(false);
                System.out.println("Okay.");
            } catch (SQLException e) {
                sqlerr(e);
            }
            return con;
        }
     
        Xid[] createXID() {
            Xid xid_1 = null;
            byte[] gid_1 = new byte[1];
            byte[] bid_1 = new byte[1];
            gid_1[0] = (Byte.decode(props.getProperty("xid.global"))).byteValue();
            bid_1[0] = (Byte.decode(props.getProperty("xid.branch.db_1"))).byteValue();
            System.out.print("Creating an XID (" + Byte.toString(gid_1[0]) + ", " + Byte.toString(bid_1[0]) + ") for DB_1: ");
            xid_1 = new MysqlXid(gid_1, bid_1, 0);
            System.out.println("Okay.");
     
            Xid xid_2 = null;
            byte[] gid_2 = new byte[1];
            byte[] bid_2 = new byte[1];
            gid_2[0] = (Byte.decode(props.getProperty("xid.global"))).byteValue();
            bid_2[0] = (Byte.decode(props.getProperty("xid.branch.db_2"))).byteValue();
            System.out.print("Creating an XID (" + Byte.toString(gid_2[0]) + ", " + Byte.toString(bid_2[0]) + ") for DB_2: ");
            xid_2 = new MysqlXid(gid_2, bid_2, 0);
            System.out.println("Okay.");
            return new Xid[]{xid_1, xid_2};
        }
     
        void execBranch(Connection con, XAResource xares, Xid xid, String sql) {
            try {
                xares.start(xid, XAResource.TMNOFLAGS);
                Statement stmt = con.createStatement();
                stmt.executeUpdate(sql);
                xares.end(xid, XAResource.TMSUCCESS);
            } catch (XAException e) {
                System.err.println("XA exception caught:");
                System.err.println("Cause  : " + e.getCause());
                System.err.println("Message: " + e.getMessage());
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (SQLException e) {
                sqlerr(e);
                throw new RuntimeException(e);
            }
        }
     
        int prepareCommit(XAResource xares, Xid xid) {
            int rc = 0;
            System.out.print("Prepare XA branch (" +
                    Byte.toString((xid.getGlobalTransactionId())[0]) + ", " +
                    Byte.toString((xid.getBranchQualifier())[0]) + "): ");
            try {
                xares.prepare(xid);
            } catch (XAException e) {
                xaerr(e);
                throw new RuntimeException(e);
            }
            System.out.println("Okay.");
            return rc;
        }
     
        void commitBranch(XAResource xares, Xid xid) {
            System.out.print("Commit XA branch (" +
                    Byte.toString((xid.getGlobalTransactionId())[0]) + ", " +
                    Byte.toString((xid.getBranchQualifier())[0]) + "): ");
            try {
                // second parameter is 'false' since we have a two phase commit
                xares.commit(xid, false);
            } catch (XAException e) {
                xaerr(e);
                throw new RuntimeException(e);
            }
            System.out.println("Okay.");
        }
     
        void rollbackBranch(XAResource xares, Xid xid) {
            System.out.print("Rollback XA branch (" +
                    Byte.toString((xid.getGlobalTransactionId())[0]) + ", " +
                    Byte.toString((xid.getBranchQualifier())[0]) + "): ");
            try {
                xares.rollback(xid);
            } catch (XAException e) {
                xaerr(e);
                throw new RuntimeException(e);
            }
            System.out.println("Okay.");
        }
     
        void sqlerr(SQLException exception) {
            System.err.println("FAILED.");
            while (exception != null) {
                System.err.println("==> SQL Exception caught");
                System.err.println("--> SQLCODE : " + exception.getErrorCode());
                System.err.println("--> SQLSTATE: " + exception.getSQLState());
                System.err.println("--> Message : " + exception.getMessage());
                exception = exception.getNextException();
            }
        }
     
        void xaerr(XAException exception) {
            System.err.println("FAILED.");
            System.err.println("==> XA Exception caught");
            System.err.println("--> Cause  : " + exception.getCause());
            System.err.println("--> Message: " + exception.getMessage());
            exception.printStackTrace();
        }
     
        public static void main (String args[]) {
            new DistributeTransaction();
        }
     
    }

    XA性能局限性

    效率低下,准备阶段的成本持久,全局事务状态的成本持久,性能与本地事务相差10倍左右;
    提交前,出现故障难以恢复和隔离问题。

    ————————————————
    版权声明:本文为CSDN博主「1Vincent」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/wuzhiwei549/article/details/79925618

  • 相关阅读:
    2017年上海金马五校程序设计竞赛:Problem C : Count the Number (模拟)
    2017年上海金马五校程序设计竞赛:Problem B : Sailing (广搜)
    2017年上海金马五校程序设计竞赛:Problem A : STEED Cards (STL全排列函数)
    之江学院第0届校赛 qwb去面试 (找规律)
    [leetcode-41-First Missing Positive]
    [leetcode-625-Minimum Factorization]
    [leetcode-623-Add One Row to Tree]
    [leetcode-624-Maximum Distance in Arrays]
    [leetcode-95-Unique Binary Search Trees II]
    [leetcode-96-Unique Binary Search Trees]
  • 原文地址:https://www.cnblogs.com/muxi0407/p/12112204.html
Copyright © 2011-2022 走看看