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倍左右;
提交前,出现故障难以恢复和隔离问题。

  • 10
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
编辑推荐: 本文来自于csdn,本篇文章主要介绍了LCN5.0.2有3种模式,分别是LCN模式,TCC模式,TXC模式,希望对您的学习 有所帮助。 一、简介 LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。 LCN模式: LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。 该模式的特点: - 该模式对代码的嵌入性为低。 - 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。 - 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。 - 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。 TCC模式: TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。 该模式的特点: - 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。 - 该模式对有无本地事务控制都可以支持使用面广。 - 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。 TXC模式: TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。 该模式的特点: - 该模式同样对代码的嵌入性低。 - 该模式仅限于对支持SQL方式的模块支持。 - 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。 - 该模式不会占用数据库的连接资源。 二、原理 核心步骤 1.创建事务组 是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。 2.添加事务组 添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给TxManager的操作。 3.关闭事务组 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager的动作。当执行完关闭事务组的方法以后,TxManager将根据事务组信息来通知相应的参与模块提交或回滚事务事务控制原理 LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的事务协调控制。 TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后TxClient连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在关闭连接。 对于代理连接池的优化 自动超时机制,任何通讯都有最大超时限制,参与模块在等待通知的状态下也有最大超时限制,当超过时间限制以后事务模块将先确认事务状态,然后再决定执行提交或者回滚操作,主要为了给最大资源占用时间加上限制。 智能识别创建不同的连接 对于只读操作、非事务操作LCN将不开启代理功能,返回本地连接对象,对于补偿事务的启动方将开启回滚连接对象,执行完业务以后马上回滚事务。 LCN连接重用机制 当模块在同一次事务下被重复执行时,连接资源会被重用,提高连接的使用率。 事务补偿机制 为什么需要事务补偿? 事务补偿是指在执行某个业务方法时,本应该执行成功的操作却因为服务器挂机或者网络抖动等问题导致事务没有正常提交,此种场景就需要通过补偿来完成事务,从而达到事务的一致性。 补偿机制的触发条件? 当执行关闭事务组步骤时,若发起方接受到失败的状态后将会把该次事务识别为待补偿事务,然后发起方将该次事务数据异步通知给TxManager。TxManager接受到补偿事务以后先通知补偿回调地址,然后再根据是否开启自动补偿事务状态来补偿或保存该次切面事务数据。 补偿事务机制 LCN的补偿事务原理是模拟上次失败事务的请求,然后传递给TxClient模块然后再次执行该次请求事务。 模拟场景演示 若存在事务发起方、参与方A、参与方B。调用关系图如下 那么他们正常执行业务的时序图为: 若参与方B出现异常,那么他们的业务时序图为: 若他们的调用关系是这样的情况 此时发生参与方B出现异常时他们的时序图为: 三、使用 环境: SpringBoot 2.0.
第1章 课程介绍 介绍该课程的内容、学习成果、实例,还有学习所需的前提知识。 1-1 导学-分布式事务实践 第2章 事务原则与实现 介绍了事务的四大原则,并通过实例介绍数据库实现事务的方法,以及使用JDBC实现事务的方法。 2-1 事务原则与实现:事务 2-2 事务原则与实现:SQL事务 2-3 事务原则与实现:JDBC事务(上) 2-4 事务原则与实现:JDBC事务(下) 第3章 使用Docker搭建环境 介绍了Docker的使用,通过Docker将课程环境搭建起来,方便那些不了解这些技术的同学之后的学习。 3-1 docker简介与mysql安装-1 3-2 docker简介与mysql安装-2 3-3 SpringBoot基础 第4章 Spring事务机制 介绍了Spring的事务机制、事物抽象、内部事务和外部事物,以及常用的几种事务管理的实现,包括DataSource、JPA、JMS、JTA都通过实例进行说明。还有XA以及两阶段提交,并通过实例演示了使用JTA,通过两阶段提交,实现多数据源的事务实现。... 4-1 Spring事务机制_基本接口 4-2 Spring事务机制_实现 4-3 Jpa事务实例 4-4 Jms事务原理 4-5 Jms-session事务实例 4-6 Jms-spring事务实例 4-7 外部事务与JTA 4-8 JTA单数据源事务实例 4-9 JTA多数据源事务实例 第5章 分布式系统 介绍了分布式系统的定义、实现原则和几种形式,详细介绍了微服务架构的分布式系统,并使用Spring Cloud框架演示了一个完整的微服务系统的实现过程。 5-1 CAP原则和BASE理论简介 5-2 分布式系统综述 5-3 SpringCloud微服务架构 5-4 实现registry 5-5 实现proxy 5-6 user服务 5-7 order服务 5-8 添加hystrix 5-9 使用feign 5-10 优化服务间调用 第6章 分布式事务实现,模式和技术 介绍分布式事务的定义、原则和实现原则,介绍使用Spring框架实现分布式事务的几种方式,包括使用JTA、Spring事务同步、链式事务等,并通过实战介绍其实现。除此以外还介绍了一些分布式事务相关的技术,如幂等性、全局一致性ID、分布式对象等。... 6-1 分布式事务介绍 6-2 spring分布式事务实现_使用JTA 6-3 spring分布式事务实现_不使用JTA 6-4 实例1-DB-DB 6-5 实例1-DB-DB.链式事务管理器 6-6 实例2-JPA-DB.链式事务管理器 6-7 实例3-JMS-DB.最大努力一次提交 6-8 分布式事务实现模式与技术 6-9 全局一致性ID和分布式对象_ 第7章 分布式事务实现:消息驱动模式 详细介绍3种分布式事务实现的模式中的消息驱动模式并通过完整实例演示了消息驱动模式下,实现微服务系统的分布式事务的完整过程。 7-1 分布式事务实现:消息驱动模式 7-2 消息驱动模式实例:设计 7-3 消息驱动模式实例:创建ticket服务 7-4 消息驱动模式实例:实现基本ticket功能 7-5 消息驱动模式实例:锁票1 7-6 消息驱动模式实例:锁票2 7-7 按消息流程实现业务 7-8 支付过程 7-9 票转移 7-10 错误处理:锁票失败 7-11 错误处理:扣费失败 7-12 并发时的错误处理 第8章 分布式事务实现:Event Sourcing模式 详细介绍了分布式事务实现的模式中的Event Sourcing模式,并通过完整实例演示了Event Sourcing模式下,实现微服务系统的分布式事务的完整过程。 8-1 事件溯源模式介绍 8-2 事件溯源模式与Axon框架-1 8-3 事件溯源模式与Axon框架-2 8-4 使用Axon框架的设计过程介绍 8-5 Axon框架-实例(上) 8-6 Axon框架-实例(下) 8-7 Saga模式和Axon Saga 8-8 聚合命令事件(上) 8-9 聚合命令事件(下) 8-10 实现saga 8-11 实现query 8-12 处理超时 8-13 并发测试 8-14 cloud-axon实例:分布式处理介绍 8-15 事件设计 8-16 事件与队列设计 8-17 实现User服务 8-18 实现Ticket服务 8-19 实现Order服务 8-20 实现读写分离 8-21 测试与并发 8-22 事件溯源模式与Axon框架总结 第9章 TCC模式和微服务架构的设计模式 本章介绍TCC模式,也对微服务系统的几种设计模式,以及这些模式下分布式事务的实现模式进行了介绍。 9-1 TCC模式介绍 9-2 微服务架构的设计模式 第10章 课程总
Seata是一款开源的分布式事务解决方案,它提供了高性能和简单易用的分布式事务服务。Seata支持AT、TCC、SAGA和XA事务模式,旨在为用户打造一站式的分布式解决方案。AT模式是Seata的一种事务模式,它基于支持本地ACID事务的关系型数据库,并通过JDBC访问数据库。在AT模式中,分支的本地事务可以在全局事务的第一阶段提交后立即释放本地事务锁定的资源。与XA事务相比,AT模式通过回滚日志的方式,在第一阶段释放对资源的锁定,降低了锁范围,提高了效率。在第二阶段提交时,Seata的整体执行机制可以大致分为两个阶段:发起方TM向TC申请开启全局事务并生成唯一的全局事务标识XID,RM向TC注册分支事务并与XID绑定,TM向TC发起XID下的所有分支事务的全局提交或回滚请求,TC汇总事务信息并决定最终是提交还是回滚,通知所有RM提交或回滚资源,最终完成分布式事务的提交或回滚。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Seata简单介绍及其原理(一)](https://blog.csdn.net/qq_29860591/article/details/103776602)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [分布式事务Seata原理](https://blog.csdn.net/a745233700/article/details/122402795)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏目 "

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值