分布式事务学习笔记

本文详细介绍了分布式事务中的SEA架构,包括TC、TM和RM的角色,以及XA、TCC、AT和SAGA四种模式的原理、优缺点和应用。重点讲解了微服务集成中的关键配置和不同模式在事务一致性、性能和代码侵入性上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分布式事务学习笔记

1.简述BASE理论的三个思想
基本可用、软状态、最终一致
2.解决分布式事务的思想和模型
全局事务:整个分布式事务
分支事务:分布式事务中包含的每个子系统的事务
最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
强一致思想:各分支事务执行完业务不要提交,等待彼此结果,然后统一提交与回滚
Seata架构
Seata 事务管理中有三个重要的角色:
TC(Transaction Coordinator)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
TM(Transaction Manager)-事务管理者:定义全局事务的范围、开启全局事务、提交或回滚全局事务
RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
在这里插入图片描述
Seata提供了四种不同的分布式事务解决方案:
XA模式: 强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC模式: 最终一致的分阶段事务模式,有业务侵入
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
SAGA模式:长事务模式,有业务侵入

SEATA 每个微服务集成的关键配置信息

在这里插入图片描述

seata:
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: SEATA_GROUP
      application: seata-tc-server
      username: nacos
      password: nacos
  tx-service-group: seata-demo # 事务组名称
  service:
    vgroup-mapping: # 事务组与cluster的映射关系
      seata-demo: SH

nacos服务名称组成包含?
namespace+group+serviceName+cluster
seata 客户端获取tc的cluster名称方式?
以tx-group-service的值为key到vgroupMapping中查找

XA模式原理
XA规范是X/Open组织定义的分布式事务处理(DTP) 标准,XA规范藐视了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。
都成功的情况:
在这里插入图片描述
有失败的情况:
在这里插入图片描述
SEATA的XA模式
seata的XA模式做了一些调整,但大体相类似:
RM一阶段的工作
1.注册分支事务到TC
2.执行分支的业务逻辑SQL但不提交
3.报告执行状态到TC
TC二阶段的工作:
TC检测各分支事务的执行状态。如果都成功,通知所有的RM提交事务,如果有失败,通知所有的RM回滚事务
RM二阶段的工作:
接收TC指令,提交或者回滚事务

一阶段:
在这里插入图片描述
二阶段:
在这里插入图片描述
XA模式的有点是什么?
事务的强一致性,满足ACID的原则
常用数据库都支持,实现简单,并且没有代码入侵。
XA模式的缺点是什么?
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差。
依赖关系型数据库实现事务

SEATA开启XA模式的配置项:
data-source-proxy-mode: XA
需要在方法上全局事务的入口加上注解:@GlobalTransactional

AT模式原理
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷
RM执行事务后提交
在这里插入图片描述
在这里插入图片描述
阶段2:回滚或者提交 undo.log
在这里插入图片描述

阶段1RM的工作:
注册分支事务
记录undo-log(数据快照)
执行业务sql并提交
报告事务状态
阶段2提交时的RM的工作:
删除undo-log即可
阶段2回滚时的RM的工作:
根据undo-log恢复数据到更新前

AT模式的原理
在这里插入图片描述
简述AT模式与XA模式最大的区别是什么?
XA模式一阶段不提交事务,锁定资源,AT模式一阶段直接提交,不锁定资源。
XA模式依赖数据库的机制实现回滚,AT模式则是利用数据快照实现数据回滚
XA强一致性,AT模式最终一致性

AT模式的脏写问题
在这里插入图片描述
AT 模式的写隔离
引入了全局锁 ,全局锁:由TC记录当前正在操作执行数据的事务,该事务持有全局锁,具备执行权。
演示时序图:
在这里插入图片描述
这样不就与XA模式是一样的么?其实…不是

数据库的锁与TC的锁存在粒度的差别,数据库锁不释放,任何人都无法访问数据。但是TC的锁,只记录操作这样表的全局事务,由SEATA管理的。如果某个事务不是由SEATA管理的,那就仍然可以操作该行记录的其他字段的,但如果仍然操作与SEATA事务中操作的同一字段呢?解决方案如下:
在这里插入图片描述
拿更新后的镜像快照值90与当前的数据库中的80的值去比较,如果一样,把数据回滚到更新前的状态,也就是before-image,如果不一样,说明中间有其他事务做了相关的操作,需要后续人为操作。(乐观锁的思想)

AT模型的优点:
一阶段完成直接提交事务,释放数据库的资源,性能比较好。
利用全局锁实现读写隔离
没有代码的侵入,框架自动的完成回滚与提交
AT模式的缺点
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式会好很多。
实现的时候需要引入相关的表,分为TC以及RM不同的设置,seata-at.sql,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库
配置项: data-source-proxy-mode: AT

TCC模式原理
TCC模式与AT模式非常类似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据的恢复,需要实现三个方法:
Try: 资源的检测和预留
Confirm: 完成资源操作业务,要求Try成功,Confirm一定能成功
Cancel: 预留资源释放,可以理解为try的反向操作
图解操作:
在这里插入图片描述
TCC模式原理的一阶段:
在这里插入图片描述
TCC模式原理的二阶段:
在这里插入图片描述
TCC模式的每个阶段是做什么?
1.Try:资源检查和预留
2.Confirm: 业务执行和提交
3.Cancel: 预留资源的释放
TCC模式的优点是什么?
一阶段完成直接提交事务,释放数据库资源,性能好。
相比AT模型,无需生成快照,无需使用全局锁,性能最强
不依赖数据库事务,而是依赖补偿机制,可以用于非事务型数据库

TCC的缺点是什么?
有代码侵入,需要人为编写try\confirm\cancel接口
软状态,事务是最终一致的
需要考虑Confirm和Cancel的失败情况,做好幂等处理。

TCC模式需要注意的问题
1.保证confirm以及cancel接口的幂等性
2.允许空回滚
3.拒绝业务悬挂
在这里插入图片描述
在这里插入图片描述
TCC 模式的实现

1.接口的定义

package cn.itcast.account.service;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

/**
 * @author: Runqiang_Jiang
 * @Time: 2024/1/28  15:08
 */

@LocalTCC
public interface AccountTccServiceDemo {

  /**
   * 从用户账户中扣款
   */
  @TwoPhaseBusinessAction(name = "deduct",commitMethod = "confirm",rollbackMethod = "cancel")
  void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
              @BusinessActionContextParameter(paramName = "money") int money);

  boolean confirm(BusinessActionContext context);

  boolean cancel(BusinessActionContext context);


}

2.接口的实现

package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.entity.AccountFreeze.State;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTccServiceDemo;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import javax.swing.RootPaneContainer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author: Runqiang_Jiang
 * @Time: 2024/1/28  15:14
 */

@Slf4j
@Service
public class AccountTccServiceDemoImpl implements AccountTccServiceDemo {

  @Autowired
  private AccountMapper accountMapper;

  @Autowired
  private AccountFreezeMapper accountFreezeMapper;

  @Override
  @Transactional
  public void deduct(String userId, int money) {
    //money数据库已设置为无符号数,所以出现负数就会报错的
    //获取事务id
    String xid = RootContext.getXID();
    //TODO 如何避免业务悬挂?
    // 判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,我要拒绝
    AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
    if(accountFreeze!=null){
      //如果有,一定是CANCEL执行过,我要拒绝
      return;

    }

    //1.扣减可用余额
      accountMapper.deduct(userId,money);
       //2.记录冻结金额,事务的状态
    AccountFreeze build = AccountFreeze.builder().userId(userId)
        .freezeMoney(money)
        .state(State.TRY)
        .xid(xid).build();
    accountFreezeMapper.insert(build);


  }

  @Override
  public boolean confirm(BusinessActionContext context) {
    //1.获取事务id
    String xid = context.getXid();
    //2.根据id删除冻结记录
    int count = accountFreezeMapper.deleteById(xid);

    return count==1;
  }

  @Override
  public boolean cancel(BusinessActionContext context) {
    //0.查询冻结记录
    AccountFreeze accountFreeze = accountFreezeMapper.selectById(context.getXid());
   //TODO 空回滚的判断,判断freeze是否为null,为null说明try没有执行,需要空回滚
    if(accountFreeze==null){
      //证明try没有执行,需要空回滚
      String userId = context.getActionContext("userId").toString();
      AccountFreeze build = AccountFreeze.builder().userId(userId)
          .freezeMoney(0)
          .state(State.CANCEL)
          .xid(context.getXid()).build();
      accountFreezeMapper.insert(build);
      return true;
    }
    //TODO 2.幂等判断
    if(accountFreeze.getState()== State.CANCEL){
        //已经处理过了一次CANCEL,无需要重复处理
        return true;
    }


    //1.恢复可用余额
    accountMapper.refund(accountFreeze.getUserId(), accountFreeze.getFreezeMoney());
    //2.将冻结金额清零,状态改为Cancel
    accountFreeze.setFreezeMoney(0);
    accountFreeze.setState(State.CANCEL);
    int count = accountFreezeMapper.updateById(accountFreeze);
    return count==1;
  }
}

3.业务分析
在这里插入图片描述
说明:TCC的使用需要结合具体的业务情景,每个分支是事务采用不同的事务模式(AT\TCC),都是可以实现的,背后依靠的是SEATA框架

Saga模式
Saga模式是SEATA提供的长事务解决方案,也分为两阶段。
一阶段:直接提交本地事务
二阶段:成功则什么都不做,失败则通过编写补偿业务来回滚

Saga模式的优点:
1.事务参与者可以基于事件驱动实现异步调用,吞吐高
2.一阶段直接提交事务,无锁,性能好
3.不用编写TCC的三个阶段,实现简单
缺点:
软状态持续时间不确定,时效差
没有锁,没有事务隔离,会有脏写。

在这里插入图片描述
四种分布式事务模式的比较
在这里插入图片描述

B站链接
分布式事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值