• MySQL分布式事务xa的介绍与使用


    1.什么是xa

    xa指的是分布式事务,传统的事务针对的是单机MySQL。在如今大数据时代下,往往需要多台MySQL组成一个集群存储数据。这种情况下,针对所有MySQL节点操作就无法保证所有MySQL的事务一致性,即只要一台MySQL上执行的事务回滚了,那么其他所有MySQL也要回滚。

    2.xa的组成

    xa包含两个重要的角色:事务管理器(Transaction Manager)和资源管理器(Resource Manager)

    事务管理器:作为协调者,与针对单机的事务们进行通信,以让他们能够保持全局事务性。事务管理器通常由客户端程序实现,如后端通过调用JDBC的api来实现事务管理器,以处理分布式事务

    资源管理器:就是指的MySQL服务,能够支持事务

    3.xa工作流程

    xa在执行全局事务的过程中使用两阶段提交:

    1. 准备阶段:此时资源管理器已经执行完sql操作但还没有提交,事务管理器通知资源管理器准备提交
    2. 提交/回滚阶段:事务管理器通知资源管理器是提交还是回滚,只要有任意一个资源管理器出现异常,则所有资源管理器都需要回滚
    4.xa语法
    4.1基本命令
    #1.开启一个xa事务,后面跟的字符串是xid,它是一个唯一的xa事务标识符
    xa start '123';
    #2.开启完xa事务后,就能执行业务sql了
    update test_table set user = 'zhuzi' where id = 1;
    #3.end代表业务sql执行完毕,后续可以开始二阶段提交工作了
    xa end '123';
    #4.第一阶段,进行准备工作
    xa prepare '123';
    #5.返回该MySQL上那些处于prepare阶段的xa事务的信息
    xa recover;
    #6.第二阶段,回滚或提交
    xa commit '123';
    #xa rollback '123';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意,这些命令如果只是在一台mysql上执行是没什么意义的,就跟普通事务一样。需要客户端程序和多台mysql配合使用,才能实现分布式事务。

    4.2执行xa recover时报错

    ERROR 1401 (XAE03): XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency

    用户没有权限,授予权限即可

    GRANT xa_recover_admin ON *.* TO root@'%';
    
    • 1
    4.3xid的组成

    xid由三部分组成

    1. gtrid:必须写,全局事务标识符
    2. bqual:可选,分支事务的标识符(一个xa事务包含多个分支事务),默认为长度为0的空字符串""
    3. formatID:可选,用于标识gtrid和bqual的格式,默认为0

    程序会将这三部分取16进制拼接起来组成xid。

    以上内容只是xid的一个规范,实际使用时xid只要不与其他xa事务的xid冲突即可

    5.java借助jdbc使用xa事务

    需要先引入jdbc的依赖,maven坐标如下:

    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.28version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    private static void testXA() {
        Connection connection1 = null;
        Connection connection2 = null;
        MysqlXAConnection xa1 = null;
        MysqlXAConnection xa2 = null;
        Statement statement1 = null;
        Statement statement2 = null;
        try {
            //获取两台MySQL的连接
            connection1 = DriverManager.getConnection("jdbc:mysql://192.168.86.111:3306/db3", "root", "123456");
            connection2 = DriverManager.getConnection("jdbc:mysql://192.168.86.112:3306/db1", "root", "123456");
            //获取XA连接 true表示打印日志
            xa1 = new MysqlXAConnection((JdbcConnection) connection1, true);
            xa2 = new MysqlXAConnection((JdbcConnection) connection2, true);
            //获取事务管理器
            XAResource resourceManager1 = xa1.getXAResource();
            XAResource resourceManager2 = xa2.getXAResource();
    
            //构造组成xid的三部分
            byte[] globalTransactionId = UUID.randomUUID().toString().replace("-", "").getBytes();
            byte[] branch1 = "分支1".getBytes();
            byte[] branch2 = "分支2".getBytes();
            int formatId = 1;
    		//获取xid对象
            MysqlXid xid1 = new MysqlXid(globalTransactionId, branch1, formatId);
            MysqlXid xid2 = new MysqlXid(globalTransactionId, branch2, formatId);
            //要执行的业务sql
            String sql1 = "insert into test(`name`, `age`) values('竹子', 23)";
            String sql2 = "insert into employee(`employee_name`, `department_id`) values('竹叶', 1)";
            //执行xa start xid命令
            resourceManager1.start(xid1, XAResource.TMNOFLAGS);
            resourceManager2.start(xid2, XAResource.TMNOFLAGS);
            //执行业务sql
            statement1 = connection1.createStatement();
            statement1.execute(sql1);
            statement2 = connection2.createStatement();
            statement2.execute(sql2);
            //执行xa end xid命令
            resourceManager1.end(xid1, XAResource.TMSUCCESS);
            resourceManager2.end(xid2, XAResource.TMSUCCESS);
    
            //执行xa prepare xid命令
            int prepare1 = resourceManager1.prepare(xid1);
            int prepare2 = resourceManager2.prepare(xid2);
            //是否只存在一台MySQL,如果只存在一台MySQL,那么就不需要进行分布式的二阶段提交了
            boolean onePhase = false;
            //都准备好了
            if (prepare1 == XAResource.XA_OK && prepare2 == XAResource.XA_OK) {
                //执行xa commit xid命令
                resourceManager1.commit(xid1, onePhase);
                resourceManager2.commit(xid2, onePhase);
            } else {
                //执行xa rollback xid命令
                resourceManager1.rollback(xid1);
                resourceManager2.rollback(xid2);
            }
        } catch (SQLException | XAException e) {
            e.printStackTrace();
        } finally {
            try {
                statement1.close();
                statement2.close();
                xa1.close();
                xa2.close();
                connection1.close();
                connection2.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    执行的日志信息如下:
    在这里插入图片描述

  • 相关阅读:
    一文搞定class的微观结构和指令
    Evaluating Open-Domain Question Answering in the Era of Large Language Models
    ROS2专题【02】:Ubuntu上安装ROS2
    BeautifulSoup4库
    【数据结构】链表与LinkedList
    @ApiModel 和 @ApiModelProperty
    基于casbin的RBAC权限实践
    mysql 递归获取内容
    【MySQL】高级数据过滤
    【HTML】HTML基础4.2(锚点链接)
  • 原文地址:https://blog.csdn.net/pyf2533931995/article/details/138152525