分布式事务之Seata讲解

2023-05-13  本文已影响0人  上善若泪

1 Seata

学习此文章前需要先 点击了解CAP,2PC,3PC,TCC

1.1 简介

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 ATTCCSAGAXA 事务模式,为用户打造一站式的分布式解决方案。

点击了解Seata搭建

1.2 架构

seata 事务管理中有三个重要的角色:

image.png

1.3 四种事务模式

在继续学习使用SEATA之前,对seata介绍中提到的分布式事务 AT、TCC、SAGA 和 XA 事务模式这些名词有必要介绍一下

1.3.1 XA

1.3.1.1 定义

XA规范是X/open组织定义的分布式事务处理(DTP)标准,XA规范描述了全局的TM与局部之间的接口,几乎所有的主流的数据库都对XA规范提供了支持。

image.png

seata的XA模式


image.png

RM一阶段工作:

TC二阶段的工作:
TC检测各分支事务执行状态

RM 二阶段的工作:
接收 TC 的指令,提交或回滚事务

1.3.1.2 优缺点

优点:

缺点:

1.3.1.3 代码中实现

seatastarter已经完成了XA模式的自动装配,实现非常简单,步骤如下
修改yml文件(每个参与事务的微服务)

image.png
seata:
  data-source-proxy-mode: XA # 开启数据库源代理的XA模式

application启动类上添加: @EnableAutoDataSourceProxy
需要事务的方法或类上添加:@GlobalTransactional(rollbackFor = Exception.class)

@GlobalTransactional
public void saveAll(){
    User user=new User();
    user.setName("test");
    user.setAge(18);
    baseMapper.insert(user);

    //feign接口
    orderFeignService.saveFeigh();
    //手动控制异常回滚
    //throw new RuntimeException("测试回滚");
}

1.3.2 AT

1.3.2.1 定义

AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的 业务 SQL,用户的 业务 SQL 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷

image.png

阶段一RM的工作:

阶段二提交时RM的工作:

阶段二回滚时RM的工作:

image.png

1.3.2.2 全局锁

1.3.2.2.1 AT模式脏写问题

两个事务同时进行时,没有做到事务隔离性,如下事务1已经减10,事务二也减10了,但是事务1异常回滚了,恢复快照,也只能恢复事务1的恢复成100。


image.png
1.3.2.2.2 全局锁

AT 模式用全局锁来解决上方问题。两个事务同时处理时,先获取全局锁的事务1会等待事务2释放DB锁,事务2获取不到全局锁,会有300ms的时间重试,一直获取不到就释放DB锁,事务1就能获取到DB锁,恢复数据了。

image.png

极端模式:
保存快照时会保存修改前和修改后的快照,万一事务一恢复数据时,发现现在的值和快照不一样,已经被动过,就会记录异常发送警告,由人工来处理


image.png

1.3.2.3 AT模式优缺点

AT模式的优点:

AT模式缺点:

1.3.2.4 与XA模式区别

与XA模式的最大区别:

1.3.2.5 代码中实现

application启动类上添加: @EnableAutoDataSourceProxy
需要事务的方法或类上添加:@GlobalTransactional(rollbackFor = Exception.class)

在XA模式配置基础下,只需修改data-source-proxy-mode: AT

seata:
  data-source-proxy-mode: AT# 开启数据库源代理的XA模式

业务表中增加undo_log` 表

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `id`  bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `branch_id` bigint(20) NOT NULL COMMENT '分支事务ID',
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '全局事务ID',
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '上下文',
  `rollback_info` longblob NOT NULL COMMENT '回滚信息',
  `log_status` int(11) NOT NULL COMMENT '状态,0正常,1全局已完成',
  `log_created` datetime(6) NOT NULL COMMENT '创建时间',
  `log_modified` datetime(6) NOT NULL COMMENT '修改时间',
   PRIMARY KEY (`id`),
  UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) 
) ENGINE = InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

1.3.3 TCC模式

1.3.3.1 定义

TCC 模式与 AC 模式非常相似,每阶段都是独立事务,不同的是 TCC 通过人工编码来实现数据恢复。需要实现三个方法:

seata中TCC工作模型:


image.png

1.3.3.2 TCC模式优缺点

TCC 模式优点:

TCC模式缺点:

1.3.3.3 代码中实现

1.3.3.3.1 业务需求

需修改如下:

1.3.3.3.2 增加表

为了实现空回滚,防止业务悬挂,以及幂等性要求。我们必须在数据库操作的同时,记录当前事务id和执行状态,为此我们设计了一张表(自己根据业务设计,主要为了存事务状态,和业务表相关字段)

DROP TABLE IF EXISTS `tcc`;
CREATE TABLE `tcc`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `business_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '业务id',
  `state` tinyint(1) NULL DEFAULT NULL COMMENT '事务状态,0:try   , 1:confirm ,   2:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
1.3.3.3.3 新建接口编写try、confirm、cancel接口
@LocalTCC
public interface InsertTCCService {
    /**
     * try逻辑,@TwoPhaseBusinessAction中的name属性要与当前方法名一致,用于指定try逻辑对应的方法
     */
    @TwoPhaseBusinessAction(name = "insert",commitMethod = "confirm",rollbackMethod = "cancel")
    void insert(@BusinessActionContextParameter(paramName = "name") String name,
                @BusinessActionContextParameter(paramName = "age")Integer age);

    /**
     * 二阶段confirm确认方法,可以另命名,倒要保证与commitMethod一致
     * @param context 上下文,可与i传递try方法的参数
     * @return
     */
    boolean confirm(BusinessActionContext context);

    /**
     *  二阶段cancel回滚方法,可以另命名,倒要保证与rollbackMethod一致
     * @param context
     * @return
     */
    boolean cancel(BusinessActionContext context);

}

新建实现类,实现上方接口,编写业务

@Service
public class InsertTCCServiceImpl extends ServiceImpl<InsertTCCMapper, Tcc>  implements InsertTCCService {
    @Autowired
    CouponService couponService;


    @Override
    public void insert(String name, Integer age) {
        // 防止业务悬挂,先判断有没有tcc表中有没有记录,如果有,一定是cancel执行过,要拒绝业务
        // 1、获取事务id
        String xid = RootContext.getXID();
        // 2、判断是否有记录
        Tcc byId = this.getById(xid);
        if (byId!=null){
            // cancel执行过,拒绝执行业务
            return;
        }

        // 业务
        TCoupon tCoupon = new TCoupon();
        tCoupon.setName(name);
        tCoupon.setAge(age);
        couponService.save(tCoupon);

        // 记录事务状态和业务id
        Tcc tcc = new Tcc();
        tcc.setBusinessId(tCoupon.getId());
        tcc.setState(0); // 0是try
        this.save(tcc);
    }

    @Override // 成功的方法
    public boolean confirm(BusinessActionContext context) {
        // 获取事务id
        String xid = context.getXid();
        // 根据id删除tcc记录
        boolean b = this.removeById(xid);
        return b;
    }

    @Override // try的反向,恢复业务修改的数据
    public boolean cancel(BusinessActionContext context) {
        // 获取事务id
        String xid = context.getXid();
        // 查询tcc表,获取我们寸的businessId
        Tcc tcc = this.getById(xid);

        // -----空回滚的判断,为null证明try没执行,需要空回滚,就是再添加一条tcc记录,state存cancel-----
        if (tcc == null){
            // 记录事务状态和业务id
            tcc = new Tcc();
            tcc.setBusinessId(context.getActionContext("name").toString()); // 从try传的参数获取,这个字段值要唯一
            tcc.setState(2); // 2是cancel
            this.save(tcc);
            return true;
        }

        // ----幂等处理,状态判断,防止重复处理-------
        if (tcc.getState() == 2){ // 已经提交过了
            return true;
        }


        // 根据businessId删除我们业务里新增的数据
        boolean b = couponService.removeById(tcc.getBusinessId());
        return b;
    }
}

需要事务的方法或类上(事务发起方TM)添加 @GlobalTransactional 注解
接口上添加 @LocalTCC 注解,注:该注解添加在接口上,而不是实现类上

方法上添加@TwoPhaseBusinessAction定义两阶段提交
该注解属性:

@BusinessActionContextParameter 加上该注解可传递参数到二阶段方法BusinessActionContext 上下文 可取到@BusinessActionContextParameter定义的参数

1.3.4 Saga模式

1.3.4.1 定义

Saga 模式是 SEATA 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

Saga 模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。
事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,可以使用 Saga 模式。

saga 模式是 seata 提供的长事务解决方案,也分为两个阶段:

1.3.4.2 Saga模型优缺点

Saga模式优点:

Saga缺点:

1.4 模式对比

1.4.1 4种模式对比

XA AT TCC SAGA
一致性 强一致性 弱一致(也是最终一致) 弱一致(也是最终一致) 最终一致
隔离性 完全隔离 基于全局锁隔离 基于资源预留隔离 无隔离
代码侵入 有,要编写三个接口 有,要编写状态机和补偿业务
性能 非常好 非常好
场景 对一致性、隔离性有高业务的需求 基于关系型数据库的大多数分布式事务场景都可以 1、对性能要求较高的业务。2、有非关系型数据库要参与的业务 1、业务流程长、业务流程多。2、参与者包含其他公司或遗留系统服务,无法提供TCC模式要求的三个接口

1.4.2 AT和TCC模式区别

一个分布式的全局事务,整体式两阶段提交(Try-[Comfirm/Cancel])的模型,在seata中,AT模式与TCC模式事实上都是基于两阶段提交实现的,它们的区别在于:

所以TCC模式就是把自定义的分支事务(提交回滚)纳入到全局事务管理中,seata中的TCC模式就是手动版的AT模式,它允许自定义两阶段的处理逻辑而不需要依赖AT模式的undo_log回滚表

参考链接:
https://blog.csdn.net/qq_48721706/article/details/122656490
https://blog.csdn.net/weixin_44152047/article/details/118110391
https://baijiahao.baidu.com/s?id=1712163514749816361&wfr=spider&for=pc

上一篇 下一篇

猜你喜欢

热点阅读