XA两阶段提交协议 - 手写XA, Atomikos 与 Spring boot 集成

本文介绍了XA两阶段提交协议,包括一阶段和二阶段提交的优缺点。接着,作者详细阐述了如何手动实现XA协议,并理解其原理。此外,文章还展示了如何在Spring Boot应用中集成Atomikos事务管理器,配置多数据源,并提供了测试代码以体验Atomikos事务管理的效果。最后,提到了检查Atomikos事务运行结果的方法及日志自定义配置。
摘要由CSDN通过智能技术生成

因为事务需要实现ACID,即原子性、一致性、隔离性、持久性,所以需要采用一定的机制来保证,通常采用的是分阶段提交的方式。
XA:XA协议,规定事务管理器和资源管理器接口,采用二阶段提交协议。

一阶段提交协议

一阶段提交协议相对简单。当然,前提是开启了事务,然后在应用程序发出提交/回滚请求后,数据库执行操作,而后将成功/失败返回给应用程序,程序继续执行。
一阶段提交协议相对简单,简单带来的优点就是,它不用再与其他的对象交互,节省了判断步骤和时间,所以在性能上是在阶段提交协议中对好的。

二阶段提交协议

一阶段提交协议有其优点,但缺点也很明显:

数据库确认执行事务的时间较长,出问题的可能性就随之增大。
如果有多个数据源,一阶段提交协议无法协调他们之间的关系。
所以在一阶段协议的基础上,有了二阶段协议,二阶段协议的好处是添加了一个管理者角色,如下:
在这里插入图片描述
很明显,二阶段协议通过将两层变为三层,增加了中间的管理者角色,从而协调多个数据源之间的关系,二阶段提交协议分为两个阶段。
为什么要分两步执行?一是因为分两步,就有了事务管理器统一管理的机会;二尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是耗时极短,耗时极短意味着操作失败的可能性也就降低。
同时,二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据。
二阶段提交协议的存在的弊端是阻塞,因为事务管理器要收集各个资源管理器的响应消息,如果其中一个或多个一直不返回消息,则事务管理器一直等待,应用程序也被阻塞,甚至可能永久阻塞。

手写XA,理解其原理

事务管理器

public class TM {
   

    public  void execute(Connection accountConn,Connection redConn) throws SQLException {
   
        //打印XA的事务日志 true 代表打印
        boolean logXaCommands = true;
        //获取RM1 的接口实例
        XAConnection xaConn1 = new MysqlXAConnection((com.mysql.jdbc.ConnectionImpl) accountConn, logXaCommands);
        XAResource rm1 = xaConn1.getXAResource();

        //获取RM2 的接口实例
        XAConnection xaConn2 = new MysqlXAConnection((com.mysql.jdbc.ConnectionImpl) redConn, logXaCommands);
        XAResource rm2 = xaConn2.getXAResource();


        //生成一个全局事务ID
        byte[] globalid = "agan12345".getBytes();
        int formatId = 1;
        try {
   
            //TM 把rm1的事务分支id,注册到全局事务ID
            byte[] bqual1 = "b00001".getBytes();
            Xid xid1 = new MysqlXid(globalid, bqual1, formatId);
            //start...end 开始 结束 rm1的本地事务
            rm1.start(xid1, XAResource.TMNOFLAGS);
            //模拟购物买一个物品,用余额支付90㡰
            String sql="update capital_account set balance_amount=balance_amount-90 where user_id=1";
            PreparedStatement ps1 = accountConn.prepareStatement(sql);
            ps1.execute();
            rm1.end(xid1, XAResource.TMSUCCESS);


            //TM 把rm2的事务分支id,注册到全局事务ID
            byte[] bqual2 = "b00002".getBytes();
            Xid xid2 = new MysqlXid(globalid, bqual2, formatId);
            //start...end 开始 结束 rm2的本地事务
            rm2.start(xid2, XAResource.TMNOFLAGS);
            //模拟购物一个物品,用红包支付10元。
            sql="update red_packet_account set balance_amount=balance_amount-10 where user_id=1";
            PreparedStatement ps2 = redConn.prepareStatement(sql);
            ps2.execute();
            rm2.end(xid2, XAResource.TMSUCCESS);

            //2阶段提交中得第一个阶段:准备提交
            int rm1_prepare = rm1.prepare(xid1);
            int rm2_prepare = rm2.prepare(xid2);

            //2阶段提交中得第二个阶段:真正提交
            if (rm1_prepare == XAResource.XA_OK && rm2_prepare == XAResource.XA_OK) {
   

                boolean onePhase = false;
                rm1.commit(xid1, onePhase);//提交事务
                rm2.commit(xid2, onePhase);//提交事务
            } else {
   //全部回滚
                rm1.rollback(xid1);
                rm1.rollback(xid2);
            }
        } catch (XAException e) {
   
            // 如果出现异常,也要进行回滚
            e.printStackTrace();
        }
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值