分布式之事务解决方案seata

一 seata介绍

1,seata简介

  seata是一款开源的分布式事务解决方案,致力于提供高性能与简单易用的分布式事务,为用户提供了 AT,TCC,SAGA和XA几种模式。

  • AT模式:无侵入式的分布式事务解决方案,适合不希望对业务进行改造的场景,但由于需要添加全局事务锁,对影响高并发系统的性能。该模式主要关注多DB访问的数据一致性,也包括多服务下的多DB数据访问一致性问题
  • TCC模式:高性能的分布式事务解决方案,适用于对性能要求比较高的场景。该模式主要关注业务拆分,在按照业务横向扩展资源时,解决服务间调用的一致性问题
  • Saga模式:长事务的分布式事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统。Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统,事务参与者可以是其它公司的服务也可以是遗留系统的服务,并且对于无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。

2,seata的核心组件

  在 Seata 中主要有以下三种角色,其中 TM 和 RM 是作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署:

  • 事务协调器(TC):维护全局事务的运行状态,负责协调并驱动全局事务的提交和回滚。
  • 事务管理器(TM):事务的发起方,控制全局事务的范围,负责开启一个全局事务,并最终发起全局事务的提交和回滚的决议。
  • 资源管理器(RM):事务的参与方,管理本地事务正在处理的资源,负责向TC注册本地事务,汇报本地事务状态,接受TC命令来驱动本地事务的提交和回滚。

3,seata的整体执行流程

分布式之事务解决方案seata_第1张图片
Seata 分布式事务的整体执行机制如上图所示,可以大致分为两阶段提交:

  1. 发起方 TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成唯一的全局事务标识 XID,该 XID 在后续事务的服务调用链路的上下文传播(通过Aop实现)
  2. RM 向 TC 注册分支事务,汇报资源准备状况,并与 XID 进行绑定(Branch分支事务指分布式事务中每个独立的本地局部事务)
  3. TM 向 TC 发起 XID 下的所有分支事务的全局提交或回滚请求(事务一阶段结束)
  4. TC 汇总事务信息,决定分布式事务是提交还是回滚;
  5. TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;

Seata 的 全局事务 处理过程,分为两个阶段:

  • 执行阶段 :执行分支事务,并保证执行结果满足是可回滚的(Rollbackable) 和持久化的(Durable)。
  • 完成阶段: 根据 执行阶段 结果形成的决议,应用通过 TM 发出的全局提交或回滚的请求给 TC,TC 命令 RM 驱动 分支事务 进行 Commit 或 Rollback。

Seata 的所谓事务模式是指:运行在 Seata 全局事务框架下的分支事务 的行为模式。准确地讲,应该叫作分支事务模式

  不同的 事务模式 区别在于 分支事务 使用不同的方式达到全局事务两个阶段的目标。即回答以下两个问题:

  • 执行阶段 :如何执行并保证执行结果满足是可回滚的(Rollbackable) 和持久化的(Durable)。
  • 完成阶段: 收到 TC 的命令后,如何做到分支的提交或回滚?

我们以AT模式为例:

  • 执行阶段:
    • 可回滚:根据 SQL 解析结果,记录回滚日志
    • 持久化:回滚日志和业务 SQL 在同一个本地事务中提交到数据库
  • 完成阶段:
    • 分支提交:异步删除回滚日志记录
    • 分支回滚:依据回滚日志进行反向补偿更新

接下来就进入重头戏,Seata四大模式的介绍。

二 seata的四大模式

1,XA模式

1.简介

  首先需要知道XA模型是什么,XA 规范早在上世纪 90 年代初就被提出,用于解决分布式事务领域的问题,他也是最早的分布式事务处理方案,因为需要数据库内部也是支持XA模式的,比如MYSQL,XA模式具有强一致性的特点,因此他对数据库占用时间比较长,所以性能比较低。

XA模式属于两阶段提交。

  1. 第一阶段:进行事务注册,将事务组册到TC中执行sql语句。
  2. 第二阶段:TC判断事务是否出错,并通知所有的事务参与者,进行事务提交和回滚。
  3. 在第一到第二阶段过程中,事务一直占有数据库锁,因此性能较低,但是保证了所有事务要么一起提交要么一起回滚,所以能实现强一致性。

  无论是AT模式还是TCC,还是SAGA模式,都是源于XA规范对某些业务无法满足的场景进行的扩展。

什么是XA协议?
  XA规范是X/OPEN组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局事务管理器和局部资源管理器之间的接口,XA规范的目的是允许多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效。

  XA 规范 使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。因为XA规范最早被提出,所以几乎所有的主流数据库都保有对XA规范的支持。

分布式事务DTP模型定义的角色如下:

  • AP:即应用程序,可以理解为使用DTP分布式事务的程序,例如订单服务、库存服务
  • RM:资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库的实例(MySql),通过资源管理器对该数据库进行控制,资源管理器控制着分支事务
  • TM:事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理实务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。

分布式之事务解决方案seata_第2张图片
  DTP模式定义TM和RM之间通讯的接口规范叫XA,简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现的2PC又称为XA方案。

  现在有应用程序(AP)持有订单库和库存库,应用程序(AP)通过TM通知订单库(RM)和库存库(RM),进行扣减库存和生成订单,这个时候RM并没有提交事务,而且锁定资源。
  当TM收到执行消息,如果有一方RM执行失败,分别向其他RM也发送回滚事务,回滚完毕,释放锁资源

  当TM收到执行消息,RM全部成功,向所有RM发起提交事务,提交完毕,释放锁资源。
分布式之事务解决方案seata_第3张图片
分布式通信协议XA规范,具体执行流程如下所示:
分布式之事务解决方案seata_第4张图片
如上图所示,具体操作流程是:

  1. AP创建了RM1,RM2的JDBC连接。
  2. AP通知生成全局事物ID,并把RM1,RM2注册到全局事务ID
  3. 执行二阶段协议中的第一阶段prepare
  4. 根据prepare请求,决定整体提交或回滚。

  但是对于XA而言,如果一个参与全局事务的资源“失联”了,那么就意味着TM收不到分支事务结束的命令,那么它锁定的数据,将会一直被锁定,从而产生死锁,这个也是Seata需要重点解决的问题。

  在Seata定义的分布式事务架构中,利用事务资源(数据局、消息)等对XA协议进行支持,用XA协议的机制来管理分支事务

分布式之事务解决方案seata_第5张图片

  • 执行阶段:
    • 可回滚:业务SQL操作在XA分支中进行,有资源管理器对XA协议的支持来保证可回滚
    • 持久化:ZA分支完成以后,执行 XA prepare,同样,由资源对XA协议的支持来保证持久化
  • 完成阶段:
    • 分支提交:执行XA分支的commit
    • 分支回滚:执行XA分支的rollback

XA存在的意义
  Seata 已经支持了三大事务模式:AT\TCC\SAGA,这三个都是补偿型事务,补偿型事务处理你机制构建在 事务资源 之上(要么中间件层面,要么应用层),事务资源本身对于分布式的事务是无感知的,这种对于分布式事务的无感知存在有一个根本性的问题,无法做到真正的全局一致性。
  例如一个库存记录,在补偿型事务处理过程中,用80扣减为60,这个时候仓库管理员查询数据结果,看到的是60,之后因为异常回滚,库存回滚到原来的80,那么这个时候库存管理员看到的60,其实就是脏数据,而这个中间状态就是补偿型事务存在的脏数据。

  和补偿型事务不同,XA协议要求事务资源 本身提供对规范和协议的支持,因为事务资源感知并参与分布式事务处理过程中,所以事务资源可以保证从任意视角对数据的访问有效隔离性,满足全局数据的一致性。

2.使用

官方案例:https://github.com/seata/seata-samples
项目名:seata-samples

业务开始: business-xa
库存服务: stock-xa
订单服务: order-xa
账号服务: account-xa

  把这个项目案例下载下来以后,找到项目名为seata-xa的目录,里面有测试数据库的链接,如果不想用测试数据库,只需要修改官方文档中数据库配置信息即可。

  首先关注的是 business-xa项目,更多的关注BusinessService.purchase()方法

      @GlobalTransactional
    public void purchase(String userId, String commodityCode, int orderCount, boolean rollback) {
        String xid = RootContext.getXID();
        LOGGER.info("New Transaction Begins: " + xid);
        
        //调用库存减库存
        String result = stockFeignClient.deduct(commodityCode, orderCount);

        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("库存服务调用失败,事务回滚!");
        }

        //生成订单
        result = orderFeignClient.create(userId, commodityCode, orderCount);

        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("订单服务调用失败,事务回滚!");
        }

        if (rollback) {
            throw new RuntimeException("Force rollback ... ");
        }
    }

  其实现方法较之前差不多,我们只需要在order-xa里面(OrderService.create),添加人为错误(int i = 1/0;)

    public void create(String userId, String commodityCode, Integer count) {
        String xid = RootContext.getXID();
        LOGGER.info("create order in transaction: " + xid);

        int i = 1/0;

        // 定单总价 = 订购数量(count) * 商品单价(100)
        int orderMoney = count * 100;
        // 生成订单
        jdbcTemplate.update("insert order_tbl(user_id,commodity_code,count,money) values(?,?,?,?)",
            new Object[] {userId, commodityCode, count, orderMoney});
        // 调用账户余额扣减
        String result = accountFeignClient.reduce(userId, orderMoney);
        if (!SUCCESS.equals(result)) {
            throw new RuntimeException("Failed to call Account Service. ");
        }

    }

  里面有一个方法可以进行XA模式和AT模式的转换OrderXADataSourceConfiguration.dataSource

    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        // DataSourceProxy for AT mode
        // return new DataSourceProxy(druidDataSource);

        // DataSourceProxyXA for XA mode
        return new DataSourceProxyXA(druidDataSource);
    }

我们启动这四个服务,访问地址 http://localhost:8084/purchase

分布式之事务解决方案seata_第6张图片
  我们可以其中报错,然后再去看对应数据库的数据,没有发生更改,说明我们的XA模式生效了,当你dubug去看里面的库存服务的时候,当操作数据更改的时候,数据库里面其实也是没有记录的,因为XA是强一致性,只有当事务结束完成以后,才会更改其中的数据。

  XA模式的加入,补齐了Seata在全局一致性场景下的缺口,形成了AT、TCC、Saga、XA 四大事务模式的版图,基本满足了所有场景分布式事务处理的需求。

其中XA和AT是无业务侵入的,而TCC和Saga是有一定业务侵入的。

2,AT模式

  先来介绍一下AT模式,AT模式是一种没有侵入的分布式事务的解决方案,在AT模式下,用户只需关注自己的业务SQL,用户的业务SQL作为一阶段,Seata框架会自动生成事务进行二阶段提交和回滚操作。

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:提交异步化,非常快速的完成。回滚通过一阶段的回滚日志进行反向补偿。

AT模式的主要特点:

  1. 最终一致性
  2. 性能较XA高
  3. 只在第一阶段获取锁,在第一阶段进行提交后释放锁。

  在一阶段中,Seata会拦截 业务SQL ,首先解析SQL语义,找到要操作的业务数据,在数据被操作前,保存下来记录 undo log,然后执行 业务SQL 更新数据,更新之后再次保存数据 redo log,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。

  相对一阶段,二阶段比较简单,负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的 undo Log ,通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。

  AT流程分为两阶段,主要逻辑全部在第一阶段,第二阶段主要做回滚或日志清理的工作。流程如下:
分布式之事务解决方案seata_第7张图片
  从上图中我们可以看到,订单服务中TM向TC申请开启一个全局事务,一般通过@GlobalTransactional标注开启,TC会返回一个全局事务ID(XID),订单服务在执行本地事务之前,RM会先向TC注册一个分支事务,
  订单服务依次生成undo log 执行本地事务,生成redo log 提交本地事务,向TC汇报,事务执行OK。

  订单服务发起远程调用,将事务ID传递给库存服务,库存服务在执行本地事务之前,先向TC注册分支事务,库存服务同样生成undo Log和redo Log,向TC汇报,事务状态成功。

  如果正常全局提交,TC通知RM一步清理掉本地undo和redo日志,如果存在一个服务执行失败,那么发起回滚请求。通过undo log进行回滚。

  在这里还会存在一个问题,因为每个事务从本地提交到通知回滚这段时间里面,可能这条数据已经被其他事务进行修改,如果直接用undo log进行回滚,可能会导致数据不一致的情况。这个时候 RM会用 redo log进行验证,对比数据是否一样,从而得知数据是否有别的事务进行修改过,undo log是用于被修改前的数据,可以用来回滚,redolog是用于被修改后的数据,用于回滚校验。如果数据没有被其他事务修改过,可以直接进行回滚,如果是脏数据,redolog校验后进行处理。

3,TCC模式

具体使用案例:https://seata.io/zh-cn/blog/integrate-seata-tcc-mode-with-spring-cloud.html

什么是TCC?
  TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),他们的具体含义如下:

  • Try:对业务资源的检查并预留;
  • Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功;
  • Cancel:对业务处理进行取消,即回滚操作,该步骤回对 Try 预留的资源进行释放。

TCC 是一种侵入式的分布式事务解决方案,以上三个操作都需要业务系统自行实现,对业务系统有着非常大的入侵性,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。
分布式之事务解决方案seata_第8张图片
TCC和AT区别
AT 模式基于 支持本地 ACID 事务 的 关系型数据库:

  • 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
  • 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

相应的,TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用自定义 的 prepare 逻辑。
  • 二阶段 commit 行为:调用自定义 的 commit 逻辑。
  • 二阶段 rollback 行为:调用自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

特点:

  • 侵入性比较强,并且需要自己实现相关事务控制逻辑
  • 在整个过程基本没有锁,性能较强

4,SAGA模式

  Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务(执行处理时候出错了,给一个修复的机会)都由业务开发实现。

  Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

  之前我们学习的Seata分布式三种操作模型中所使用的的微服务全部可以根据开发者的需求进行修改,但是在一些特殊环境下,比如老系统,封闭的系统(无法修改,同时没有任何分布式事务引入),那么AT、XA、TCC模型将全部不能使用,为了解决这样的问题,才引用了Saga模型。

  比如:事务参与者可能是其他公司的服务或者是遗留系统,无法改造,可以使用Saga模式。
分布式之事务解决方案seata_第9张图片
  Saga模式是Seata提供的长事务解决方案,提供了异构系统的事务统一处理模型。在Saga模式中,所有的子业务都不在直接参与整体事务的处理(只负责本地事务的处理),而是全部交由了最终调用端来负责实现,而在进行总业务逻辑处理时,在某一个子业务出现问题时,则自动补偿全面已经成功的其他参与者,这样一阶段的正向服务调用和二阶段的服务补偿处理全部由总业务开发实现。
Saga状态机
  目前Seata提供的Saga模式只能通过状态机引擎来实现,需要开发者手工的进行Saga业务流程绘制,并且将其转换为Json配置文件,而后在程序运行时,将依据子配置文件实现业务处理以及服务补偿处理,而要想进行Saga状态图的绘制,一般需要通过Saga状态机来实现。

基本原理:

  • 通过状态图来定义服务调用的流程并生成json定义文件
  • 状态图中一个节点可以调用一个服务,节点可以配置它的补偿节点
  • 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚
  • 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

Saga状态机的应用
官方文档地址:https://seata.io/zh-cn/docs/user/saga.html

Seata Safa状态机可视化图形设计器使用地址:https://github.com/seata/seata/blob/develop/saga/seata-saga-statemachine-designer/README.zh-CN.md

5,总结

  总的来说在Seata的中AT模式基本可以满足百分之80的分布式事务的业务需求,AT模式实现的是最终一致性,所以可能存在中间状态,而XA模式实现的强一致性,所以效率较低一点,而Saga可以用来处理不同开发语言之间的分布式事务,所以关于分布式事务的四大模型,基本可以满足所有的业务场景,其中XA和AT没有业务侵入性,而Saga和TCC具有一定的业务侵入

参考文献:
https://muxiaonong.blog.csdn.net/article/details/125584031
https://blog.csdn.net/a745233700/article/details/122402795
AT模式详解:
https://blog.csdn.net/qq_20161461/article/details/120388215

你可能感兴趣的:(分布式,分布式事务,XA模式,AT模式,TCC模式,SAGA模式)