mysql分布式事务(代码片段)

大忽悠爱忽悠 大忽悠爱忽悠     2022-10-20     558

关键词:

Mysql分布式事务


XA协议

为了规范分布式事务的管理,X/OPEN 提出了分布式事务处理规范XA协议,XA规范了TM与RM之间的通信接口,在TM与多个RM之间形成一个双向通信桥梁,从而在多个数据库资源下保证ACID四个特性。目前知名的数据库,如Oracle, DB2,mysql等,都是实现了XA接口的,都可以作为RM。

XA是数据库的分布式事务,强一致性,在整个过程中,数据都处于被锁住的状态,即从prepare到commit、rollback的整个过程中,TM一直拥有参与分布式事务RM对应的数据库的锁,如果有其他人要修改数据库的该条数据,就必须等待锁的释放,存在长事务风险。


分布式事务模型

X/Open定义了分布式事务处理模型,包括应用程序AP、事务管理器TM、资源管理器RM、通信资源管理器CRM。


在XA规范中分布式事务有AP、RM、TM组成:

  • 应用程序(Application Program):定义事务边界(定义事务开始和结束)并访问事务边界内的资源
  • 资源管理器(Resource Manager):RM管理计算机共享的资源,资源包含比如数据库、文件系统等
  • 事务管理器(Transaction Manager,简称TM):负责管理全局事务,分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚、失败恢复等。

流程

分布式事务的基本流程如下:


具体流程如下:

  1. 配置TM,将RM注册到TM
  2. AP从TM获取资源管理器的代理,获取TM所管理的RM的JDBC连接
  3. AP向TM发起全局事务
  4. TM将XID通知到各RM
  5. AP通过Connection连接直接对RM进行操作
  6. AP结束全局事务
  7. TM会通知RM全局事务结束
  8. 开始二阶段提交

两阶段提交

当每个RM都结束了全局事务的执行后,此时每个RM管理的分布式事务分支还没有提交,只是把该事务管理的业务逻辑执行完了。

进入二阶段提交阶段,在这个阶段,会先进入prepare阶段,然后再是commit或者rollback阶段。

具体流程如图:


第一阶段分为两个步骤:

  • 事务管理器通知参与该事务的各个资源管理器,通知他们开启事务、执行SQL(暂不提交),并进入prepare状态(该状态下可执行commit/ rollback)。
  • 资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将是否就绪的消息返回给事务管理器
  • RM根据自己的情况,如果判断自己进行的工作可以被提交,那就就对工作内容进行持久化,并给TM回执OK;否者给TM的回执NO
  • RM在发送了否定答复并回滚了已经的工作后,就可以丢弃这个事务分支信息了

第二阶段也分为两个步骤:

  • 事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否则发送提交命令。
  • 各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器。

两阶段提交的好处是有了事务管理器进行统一管理,让事务在提交前尽可能的完成所有能完成的工作。同时两阶段提交可以保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作都会被日志记录,为出现故障后的恢复提供依据。


Mysql中的XA语法

Mysql中分布式操作的基本模板如下:

开启xa事务,XA start <xid>
DML语句,即SQL增删改查语句
终止XA事务,XA end <xid>
预提交事务, XA prepare <xid>,这一步是有返回值的
提交,XA commit <xid>,根据prepare操作的返回结果做的处理
回滚,XA rollback <xid>,根据prepare操作的返回结果做的处理
XA RECOVER: 返回当前数据库中处于PREPARE状态的分支事务的详细信息

每个事务必须有一个唯一的xid值,因此当前值不能被其他XA事务使用,xid是一个XA事务标识符,用来唯一标识一个分布式事务。

xid值可以由客户端提供,或者由Mysql服务器生成。

xid基本格式如下:

xid: gtrid [,bqual] [, formatID]
  • gtrid 是一个分布式事务标识符,相同的分布式事务使用相同的gtrid,这样可以明确知道XA事务属于哪一个分布式事务
  • bqual是一个分支限定符,默认为空串,对于一个分布式事务中的每个分支事务,bqual必须是唯一的
  • formatID是一个数字,用于标识由gtrid和bqual值使用的格式,默认为1

XA语法中用到的xid值都必须和START操作使用的xid值相同


使用演示


上面给出的是命令行方式的演示过程,下面再给出java代码的使用过程:

    public MysqlXADataSource xaDataSource(String url,String uname,String pwd)
        MysqlXADataSource xa = new MysqlXADataSource();
        xa.setUrl(url);
        xa.setUser(uname);
        xa.setPassword(pwd);
        return xa;
    
    @Test
    public void testXA()
        MysqlXADataSource testXA = xaDataSource("jdbc:mysql://xxx:3306/test", "root", "xxx");
        MysqlXADataSource trainingXA = xaDataSource("jdbc:mysql://xxx:3306/training", "root", "xxx");
        try 
            //获取每个分支事务的连接
            XAConnection test = testXA.getXAConnection();
            XAResource testXAResource = test.getXAResource();
            Connection testConn= test.getConnection();
            Statement testStatement = testConn.createStatement();

            XAConnection train = trainingXA.getXAConnection();
            XAResource trainXAResource = train.getXAResource();
            Connection trainConn = train.getConnection();
            Statement trainStatement = trainConn.createStatement();

            //生成每个分支事务对应的XID
            MysqlXid testXid = new MysqlXid("test".getBytes(StandardCharsets.UTF_8), "testDB".getBytes(StandardCharsets.UTF_8), 1);
            MysqlXid trainXid = new MysqlXid("test".getBytes(StandardCharsets.UTF_8), "trainDB".getBytes(StandardCharsets.UTF_8), 1);


            //事务分支1关联分支事务sql语句
            testXAResource.start(testXid,XAResource.TMNOFLAGS);
            testStatement.execute("UPDATE stu SET classId=3 WHERE id=1");
            testXAResource.end(testXid,XAResource.TMSUCCESS);

            //事务分支2关联分支事务sql语句
            trainXAResource.start(trainXid,XAResource.TMNOFLAGS);
            trainStatement.execute("UPDATE Salers SET SNO=\\"123\\" WHERE SNAME=\\"123\\"");
            trainXAResource.end(trainXid,XAResource.TMSUCCESS);

            //两阶段提交协议第一阶段
            int testRes = testXAResource.prepare(testXid);
            int trainRes = trainXAResource.prepare(trainXid);

            //两阶段提交协议第二阶段
            if(XAResource.XA_OK==testRes && XAResource.XA_OK==trainRes)
                //存储引擎级别事务提交
                testXAResource.commit(testXid,false);
                trainXAResource.commit(trainXid,false);
            else 
                testXAResource.rollback(testXid);
                trainXAResource.rollback(trainXid);
            
         catch (SQLException | XAException e) 
            e.printStackTrace();
        
    

上面代码中出现了一些XAResource标记,这里解释一下:

  • XAResource#start方法开启一个分支事务

下面提到的名词都是XAResource类中的常量,分别对应一个整数值

XAResource:
void start(Xid xid, int flags) throws XAException;

如果这里标记传入的是TMJOIN,会尝试去加入上一个被RM记录的事务中去。如果TMRESUME被设置了,会尝试去恢复xid与自己相同的并且是被挂起的事务分支。

如果没有设置上面两个标记,并且还找到了一个分支事务并且该分支事务xid与自己相同,那么会抛出异常

一般无特殊情况推荐使用TMNOFLAGS,表示不设置任何标志


  • XAResource#end方法结束当前分支事务的执行
XAResource:
void end(Xid xid, int flags) throws XAException;

这里标记的设置分为了三种情况:

  • TMSUCCESS: 该分支事务已经成功完成了
  • TMFAIL: 该分支事务执行失败,RM会标记当前事务为回滚状态
  • TMSUSPEND: 会将当前分支事务临时挂起进入未完成状态,当前事务被挂起后需要通过start方法设置TMRESUME来恢复

  • XAResource#commit方法提交当前分支事务
XAResource:
void commit(Xid xid, boolean onePhase) throws XAException;

onePhase标志是否为一阶段提交,两阶段提交协议中,如果只有一个RM参与,那么可以优化为一阶段提交。

相当于跳过了prepare一阶段提交,变成了局部事务的处理方式


XA状态转换图



XA的BUG

在Mysql 5.5之前的版本中,如果分支事务到达prepare状态,此时数据库异常重启后,可以选择对分支事务进行提交或者回滚,但是即使选择提交事务,该事务也不会被写入BINLOG日志,这会导致在使用BINLOG恢复数据时,丢失部分数据,并在如果存在从库,可能导致主从数据库的数据不一致。

此外,如果是分支事务的客户端连接异常终止的话,例如执行prepare之后退出连接,那么数据库会自动回滚未完成的事务,之所以这样做是因为对于prepare的事务,MySQL 是不会记录binlog的(官方说是减少fsync, 起到了优化的作用)。只有当分布式事务提交的时候才会把前面的操作写入binlog信息,所以对于binlog来说,分布式事务与普通的事务没有区别,而prepare以
前的操作信息都保存在连接的I0 CACHE中,如果这个时候客户端退出了,以前的binlog信息都会被丢失,再次重连后允许提交的话,会造成Binlog丢失,从而造成主从数据的不一致,所以官方在客户端退出的时候直接把已经prepare的事务都回滚了!

但是如果分布式事务情况下,其他分支事务都成功提交,这个分支回滚,会导致分布式事务的不完整,丢失部分分支事务内容。

MySql 5.7中做了以下优化: 在session断开和实例崩溃的情况下,事务都不会自动回滚,同时在XA PREPARE时,之前的事务信息就会被写入到BINLOG并同步到从库,最终再由用户决定事务回滚或者提交。


XA的性能问题

  • XA事务和本地事务以及锁表操作是互斥的,因为XA事务会锁住当前表
  • 开启了xa事务就无法使用本地事务和锁表操作
  • 开启了本地事务就无法使用xa事务

总结

1)在执行分支事务时,会将RM资源锁住,需要等到所有的RM响应,等到第二阶段执行完毕时(提交/回滚),RM的锁才会释放,在高并发场所不适用。

2)XA方案依赖于本地数据库对XA协议的支持,如果本地数据库不支持XA协议那么第三方程序(Java)将操作不了。例如许多非关系型数据库并没有支持XA。

3)MySQL对XA方案支持的不太友好,MySQL的XA实现,没有记录prepare阶段日志。


参考资源

数据库系列之MySQL分布式事务原理及实现

对XA协议的认识

《分布式事务系列教程-第四章-XA分布式事务解决方案》

mysql2阶段提交具体实现_深入理解二阶段提交协议(DDB对XA悬挂事务的处理分析)(一)…

分布式事务实战—XA两阶段提交(2PC)方案详解

书籍: 深入浅出MySQL,高性能MySQL,Innodb技术内幕

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

mysql事务的实现原理(代码片段)

《深入理解分布式事务》第二章MySQL事务的实现原理文章目录《深入理解分布式事务》第二章MySQL事务的实现原理一、RedoLog1.RedoLog基本概念2.RedoLog基本原理3.RedoLog刷盘规则4.RedoLog写入机制5.RedoLog的LSN机制6.RedoLog相关参数二、UndoLog... 查看详情

一文搞懂mysqlxa如何实现分布式事务(代码片段)

一文搞懂MySQLXA如何实现分布式事务前言XA协议如何通过MySQLXA实现分布式事务前言MySQL支持单机事务的良好表现毋庸置疑,那么在分布式系统中,涉及多个节点,MySQL又是如何实现分布式事务的呢?比如开发一个业... 查看详情

一文搞懂mysqlxa如何实现分布式事务(代码片段)

一文搞懂MySQLXA如何实现分布式事务前言XA协议如何通过MySQLXA实现分布式事务前言MySQL支持单机事务的良好表现毋庸置疑,那么在分布式系统中,涉及多个节点,MySQL又是如何实现分布式事务的呢?比如开发一个业... 查看详情

一文搞懂mysqlxa如何实现分布式事务(代码片段)

一文搞懂MySQLXA如何实现分布式事务前言XA协议如何通过MySQLXA实现分布式事务前言MySQL支持单机事务的良好表现毋庸置疑,那么在分布式系统中,涉及多个节点,MySQL又是如何实现分布式事务的呢?比如开发一个业... 查看详情

springcloudalibabaseata分布式事务解决方案(代码片段)

SpringCloudAlibabaSeata分布式事务解决方案一、分布式事务问题二、Seata简介三、Seata的部署3.1SeataServer端配置3.1.1修改配置文件3.1.2MySQL数据库配置3.1.3启动SeataServer端3.2SeataClient客户端配置3.2.1业务前置准备3.2.2创建undo_log表3.2.3SeataClient... 查看详情

浅谈分布式事务与tx-lcn(代码片段)

最近做项目使用到了分布式事务,下面这篇文章将给大家介绍一下对分布式事务的一些见解,并讲解分布式事务处理框架TX-LCN的执行原理,初学入门,错误之处望各位不吝指正。什么情况下需要使用分布式事务?使用的场景很多... 查看详情

分布式事务(代码片段)

在说分布式事务之前,先回顾下事务的相关知识点。事务概念事务指的是一系列数据库操作,它是保证数据库正确性的基本逻辑单元,拥有ACID四个特性:原子性、一致性、隔离性与持久性。举个例子,下面这两种组成情况都叫... 查看详情

springcloud分布式事务实战编写第二个微服务(代码片段)

(1)创建工程(2)添加jarpom.xml添加:springboot父,mysql连接,(mybatis,spring-mybatisspringboot,阿里连接池),服务中心客户端。<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSc 查看详情

分布式事务(代码片段)

title:分布式事务tags:[分布式事务]date:2020/1/1620:26:25categories:java分布式事务基础概念本地事务关系型数据库,ACID产生分布式的场景分布式服务(跨网络),单应用分布式数据库(多数据源),跨jvm服务传统分布式事务模型产品模型... 查看详情

数据库mysql的完全使用说明(代码片段)

...量会话变量用户变量MySQL数据库事件语法操作MySQL数据库分布式操作MySQL 查看详情

从零开始搭建k8s集群——使用kubesphere管理平台搭建一个高可用的分布式事务服务组件seata(代码片段)

前言Seata是一款开源的分布式事务解决方案组件,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。官方原话。官方文档地址:https:... 查看详情

sqlmssql分布式事务(代码片段)

查看详情

springcloudalibabaseata分布式事务(代码片段)

...如何保证多个服务中数据的一致性呢?这样就出现了分布式事务,而Seata就是为微服务架构而生的一种高性能、易于使用的分布式事务解决方案。Seata中有三个基础组件:TransactionCoordinator(TC协调者):维护全局和分... 查看详情

分布式事务详解(代码片段)

最近面临面试,抽空了解了下分布式事务,这篇主要介绍一下分布式事务相关的知识点以及现目前最流行的分布式事务解决工具seata。OK,进入正题目录1.本地事务1.1 什么是本地事务1.2 本地事务如何保证ACID1.2.1 undo... 查看详情

springcloud分布式事务实战准备数据库和创建第一个微服务(代码片段)

1)数据库准备在数据库中mysql分别创建2个数据库forum1和forum2forum1下创建表blockforum2下创建表Themesql代码如下:1数据库1Forum1:CREATEDATABASEIFNOTEXISTSforum1;USEforum1;DROPTABLEIFEXISTSblock;CREATETABLEblock(idint(11)NOTNULLAUTO_INCREMEN 查看详情

seata:分布式事务(代码片段)

SpringCloudAlibabaSeata:分布式事务一、Seata简介1.Seata的由来​随着互联网项目的发展,分布式架构的项目显示出了独特的独特的魅力。但是分布式事务问题在分布式架构项目中越显重要。​2019年1月阿里巴巴团队发起了开源项目Fesca... 查看详情

分布式事务(代码片段)

事务介绍(引用百度百科):  数据库事务(DatabaseTransaction),是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更... 查看详情

分布式事务处理方案,微服事务处理方案(代码片段)

微服事务处理方案(分布式事务处理方案)1.什么是事务由一组操作构成的可靠、独立的工作单元。事务具有以下特点:?Atomicity(原子性)?Consistency(一致性)?Isolation(隔离性)?Durability(持久性)2.事务的一致性单体应用可以在数据库的... 查看详情