分布式事务解决方案及实现

一、事务的ACID原则

  数据库事务的几个特性:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily),简称就是ACID。

  • 原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
  • 一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
  • 隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。只有在事务确定正确提交之后,才会显示该事务对数据的改变。其他事务才能获取到这些改变后的数据。
  • 持久性:当事务正确完成后,它对于数据的改变是永久性的。

二、什么是分布式事务

  事务在单系统中的表现:多次数据库操作用事务进行管理,来保证ACID原则。

        

  但是如果各个模块都是单独独立出来的微服务,进行了分布式部署,单系统里的事务将不能保证各个数据库操作的一致性,因此就需要分布式事务来进行统一管理。

        

三、分布式事务实现方案

  现在的分布式事务实现方案有多种,有些已经被淘汰,如基于XA的两段式提交、TCC解决方案,还有本地消息表、MQ事务消息,还有一些开源的事务中间件,如LCN、GTS。

  1、基于XA的两阶段提交方案

  XA 它包含两个部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如 Oracle、DB2 这些商业数据库都实现了 XA 接口,而事务管理器作为全局的协调者,负责各个本地资源的提交和回滚。

  两阶段提交方案应用非常广泛,几乎所有商业OLTP (On-Line Transaction Processing)数据库都支持XA协议。但是两阶段提交方案开发复杂、锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

        

  2、TCC解决方案

  TCC方案在电商、金融领域落地较多。TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。

  • Try 阶段主要是对业务系统做检测及资源预留,完成业务的准备工作。

  • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。

  • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

  基本原理如下图所示:

        

  事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。

  微服务倡导服务的轻量化、易部署,而TCC方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大。

  3、本地消息表 (异步确保)

  本地消息表是国外的 ebay 搞出来的一套方案,如图所示:

          

  我们首先需要在本地数据新建一张本地消息表,然后我们必须还要一个MQ(不一定是mq,但必须是类似的中间件)。

  消息表怎么创建呢?这个表应该包括这些字段: id, biz_id, biz_type, msg, msg_result, msg_desc,atime,try_count。分别表示uuid,业务id,业务类型,消息内容,消息结果(成功或失败),消息描述,创建时间,重试次数, 其中biz_id,msg_desc字段是可选的。

  实现思路为:

  • A 系统在自己本地一个事务里操作同时,插入一条数据到消息表;
  • 接着 A 系统将这个消息发送到 MQ 中去;
  • B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息
  • B 系统执行成功之后,就会更新自己本地消息表的状态以及 A 系统消息表的状态;
  • 如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
  • 这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。

  这个方案严重依赖于数据库的消息表来管理事务,这样在高并发的情况下难以扩展,同时要在数据库中额外添加一个与实际业务无关的消息表来实现分布式事务,繁琐。

  4、MQ事务消息

  直接基于 MQ 来实现事务,不再用本地的消息表。有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

             

  实现思想为:

  • A 系统先发送一个 prepared 消息到 mq,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;
  • 如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 mq 发送确认消息,如果失败就告诉 mq 回滚消息;
  • 如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;
  • mq 会自动定时轮询所有 prepared 消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。
  • 这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。

  这种方案缺点就是实现难度大,而且主流MQ不支持。

  5、分布式事务中间件解决方案

  分布式事务中间件其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。典型代表有:阿里的GTS(https://www.aliyun.com/aliware/txc)、开源应用LCN。

  其实现原理如下:

          

四、LCN分布式事务框架

  1、LCN框架的由来

  在设计框架之初的1.0 ~ 2.0的版本时,框架设计的步骤是如下的,各取其首字母得来的LCN命名。

  锁定事务单元(lock)、确认事务模块状态(confirm)、通知事务(notify)

  2、LCN框架相关资料

tx-lcn官方地址:https://www.txlcn.org/
tx-lcn Github地址:https://github.com/codingapi/tx-lcn
tx-lcn服务下载地址:https://pan.baidu.com/s/1cLKAeE#list/path=%2F
tx-lcn服务源码地址:https://github.com/codingapi/tx-lcn/tree/master/tx-manager

  3、LCN框架核心执行步骤

创建事务组:是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。

添加事务组:添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给TxManager的操作。

关闭事务组:是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager的动作。当执行完关闭事务组的方法以后,TxManager将根据事务组信息来通知相应的参与模块提交或回滚事务。

  如图所示,如果调用顺序为A{业务A,B{业务B}}这种A包含B的方式,那么正常调用时序图为:

          

  如果B发生异常,那么时序图为:

          

  如果调用顺序为A{业务A},B{业务B}这种A先于B的方式,那么B发生异常时的时序图为:

          

  4、LCN应用

  4.1、搭建tx-manager服务

  LCN是通过一个独立的微服务tx-manager作为分布式事务控制服务端(事务协调器)。需要执行分布式事务控制的微服务应用都通过远程服务调用的方式,在tx-manager上标记事务组,在执行事务处理后,将本地事务状态发送到tx-manager中对应的事务组上,tx-manager会根据具体的状态来通知相应的微服务应用提交或回滚。

  tx-manager也是使用Spring Cloud开发的一个微服务应用,在搭建过程上是非常简单的。下载tx-manager事务协调器zip压缩包:https://pan.baidu.com/s/1cLKAeE#list/path=%2F
压缩包解压后内容如下:

          

  修改application.properties配置文件,提供本地微服务应用的Eureka注册中心配置、redis配置。其中redis是事务协调器在处理事务组时使用的临时存储。

##########################txmanager-start########################服务端口server.port=8899#tx-manager不得修改spring.application.name=tx-managerspring.mvc.static-path-pattern=/**spring.resources.static-locations=classpath:/static/###########################txmanager-end######################## eureka地址:由于txManager也是一个SpringCloud的微服务,因此也要注册服务,交由Eureka Server管理eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/eureka.instance.prefer-ip-address=true#############################redis-start########################### redis 单点环境配置:LCN采用Redis记录事务状态组状态,并基于事务组状态管理分布式事务# redisspring.redis.database=0spring.redis.timeout=0spring.redis.host=192.168.1.136spring.redis.port=6379spring.redis.pool.max-active=100spring.redis.pool.max-wait=3000spring.redis.pool.max-idle=200spring.redis.pool.min-idle=50spring.redis.pool.timeout=600##############################redis-end#########################################################LCN-start########################tm.transaction.netty.delaytime = 5tm.transaction.netty.hearttime = 15tm.redis.savemaxtime=30tm.socket.port=9999tm.socket.maxconnection=100tm.compensate.auto=falsetm.compensate.notifyUrl=http://ip:port/pathtm.compensate.tryTime=30tm.compensate.maxWaitTime=5000logging.level.com.codingapi=debug

  将修改后的application.properties配置文件打包到tx-manager-x.x.x.jar中,替代jar中原有的默认配置文件。

  使用命令: java -jar tx-manager-x.x.x.jar启动微服务。

  测试tx-manager事务协调器是否启动成功可以访问http://ip:8899/。如下结果代表事务协调器启动成功:

  4.2、在微服务中使用LCN实现分布式事务管理

  在所有需要处理分布式事务的微服务中增加下述依赖:为统一资源版本,使用properties统一管理版本信息。

<properties>    <lcn.last.version>4.1.0</lcn.last.version></properties><dependency>    <groupId>com.codingapi</groupId>    <!-- 此例采用SpringCloud,如果是dubbo需要引用另一个包 -->    <artifactId>transaction-springcloud</artifactId>    <version>${lcn.last.version}</version>    <exclusions>        <exclusion>            <groupId>org.slf4j</groupId>            <artifactId>*</artifactId>        </exclusion>    </exclusions></dependency><dependency>    <groupId>com.codingapi</groupId>    <artifactId>tx-plugins-db</artifactId>    <version>${lcn.last.version}</version>    <exclusions>        <exclusion>            <groupId>org.slf4j</groupId>            <artifactId>*</artifactId>        </exclusion>    </exclusions></dependency>

  在全局配置文件中增加下述配置:

# 定义事务协调器所在位置。根据具体环境定义其中的IP地址和端口。tm.manager.url=http://127.0.0.1:8899/tx/manager/

  使用LCN做分布式事务管理时,微服务应用内必须提供一个用于获取txUrl(txUrl就是全局配置文件中定义的tm.manager.url)的类型实现,这个类可以使用独立应用定义,在微服务应用中引入。具体如下:

@Servicepublic class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {    @Value("${tm.manager.url}")    private String url;    @Override    public String getTxUrl() {        return url;    }}

  在分布式事务管理代码中增加注解@TxTransaction。在业务调用方增加的注解需要属性isStart=true。而被调用方则不需要定义任何的注解属性。如:

  A服务调用了B服务,那么A服务中代码:

@TxTransaction(isStart=true)@Transactionalpublic void trade() {    //本地调用    tradeDao.save();    //远程调用方    orderService.order();}

  B服务中代码:

@Transactional@TxTransactionpublic void order() {    //本地调用    orderDao.save();}

  其中@Transactional用于管理本地事务,而@TxTransaction管理分布式事务,当需要回滚时,调用本地自带的事务管理器进行回滚。

(0)

相关推荐

  • 微服务分布式事务之LCN、TCC特点、事务补偿机制缘由以及设计重点

    在亿级流量架构之分布式事务解决方案对比中, 已经简单阐明了从本机事务到分布式事务的演变过程, 文章的最后简单说明了TCC事务, 这儿将会深入了解TCC事务是原理, 以及理论支持, 最后会用Demo举例 ...

  • 分布式缓存的选择

    架构师(JiaGouX) 我们都是架构师! 架构未来,你来不来? XA/二阶段提交 基于XA协议的二阶段提交 所谓的 XA 方案,即:两阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器 ...

  • 分布式事务最经典的七种解决方案!

    优质文章,第一时间送达 随着业务的快速发展.业务复杂度越来越高,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构.随之而来就必然遇到分布式事务这个难题,这篇文章总结了分布式事务最经典的解决 ...

  • 分布式事务有这一篇就够了

    分布式事务有这一篇就够了

  • 《我想进大厂》之分布式事务篇

    对于分布式事务,相信所有人都应该很了解,为什么会有分布式事务?无论是数据量导致的分库,还是现在微服务盛行的场景都是他出现的原因. 这一篇内容还是避免不了俗套,主要的范围无非是XA.2PC.3PC.TC ...

  • 聊聊分布式事务,再说说解决方案

    来源:https://www.cnblogs.com/savorboard/p/7679902.html 作者:Savorboard 前言 最近很久没有写博客了,一方面是因为公司事情最近比较忙,另外一 ...

  • 分布式事务概述及大厂通用解决方案

    分布式事务概述及大厂通用解决方案

  • 分布式事务的四种解决方案

    例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务. 解决方案 在分布式系统中,要实现分布式事务,无外乎那几种解决方案. 一.两阶段提交(2PC) 两阶段提交(Two-phase Co ...

  • 分布式事务之解决方案(最大努力通知)

    6.1. 什么是最大努力通知 最大努力通知也是一种解决分布式事务的方案,下边是一个是充值的例子: 交互流程 : 1.账户系统调用充值系统接口 2.充值系统完成支付处理向账户系统发起充值结果通知 若通知 ...

  • 分布式事务之解决方案(可靠消息最终一致性)

    5. 分布式事务解决方案之可靠消息最终一致性 5.1. 什么是可靠消息最终一致性事务 可靠消息最终一致性方案是指当事务发起执行完全本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处 ...

  • 分布式事务之解决方案(XA和2PC)

    3. 分布式事务解决方案之2PC(两阶段提交) 针对不同的分布式场景业界常见的解决方案有2PC.TCC.可靠消息最终一致性.最大努力通知这几种. 3.1. 什么是2PC 2PC即两阶段提交协议,是将整 ...

  • 分布式事务:两阶段提交与三阶段提交

    在分布式系统中著有 CAP 理论,该理论由加州大学伯克利分校的 Eric Brewer 教授提出,阐述了在一个分布式系统中不可能同时满足 一致性(Consistency).可用性(Availabili ...

  • 分布式事务+DDD+负载均衡+服务治理已撸!微服务不就这点事?

    Go语言中文网 今天 最近有看到"微服务,分久必合.合久必分"的言论,我同意,微服务不是架构演变的终点,细说还有Serverless.FaaS等方向.但纠结要不要拆分是没有必要的, ...