Seata分布式事务实战指南:4种模式详解与避坑指南

一、什么是分布式事务?

在分布式系统中,当一个业务跨越多个服务或数据源时,每个服务作为一个分支事务。为了确保所有分支事务的最终状态一致,就需要使用分布式事务。
场景案例:电商下单流程涉及订单、库存、账户3个服务,需保证:

  • 订单创建成功
  • 库存扣减成功
  • 账户扣款成功
    任一服务失败,所有操作必须回滚——这就是分布式事务的核心需求。
    在这里插入图片描述

二、分布式事务核心理论

CAP定理

1998年,加州大学的计算机科学家Eric Brewer提出,分布式系统有三个指标:Consistency ( 一致性)、Availability (可用性)、Partition tolerance (分区容错性)
Eric Brewer说,分布式系统无法同时满足这三个指标。这个结论就叫做CAP定理。
在这里插入图片描述

特性说明生活类比
C一致性所有节点数据实时一致银行ATM机与柜台余额一致
A可用性任何请求都能快速响应医院急诊科24小时接诊
P分区容错性网络故障时系统仍能运行地铁某线路故障,其他线路照常

总结:
分布式系统通过网络连接,一定会出现分区问题( P);当分区出现时,系统的一致性(C)和可用性(A)就无法满足
现实选择:

  • CP模式(如银行系统):宁可暂时不可用,也要保证数据绝对一致
  • AP模式(如电商库存):允许短暂库存超卖,优先保证服务可用
    思考:elasticsearch集群是CP还是AP?
    ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其它节点,保证数据一致。因此是低可用性,高一致性,属于CP

BASE理论: 柔性事务解决方案

BASE 理论是对 CAP 的补充,强调:

  • Basically Available (基本可用) :允许在故障时部分损失可用性,即保证核心可用(如双11期间关闭评价功能)。
  • Soft State (软状态) :在一定时间内,允许出现中间状态,比如临时的不一致状态(如"支付中"状态)。
  • Eventually Consistent (最终一致性) :在软状态结束后,通过重试/补偿机制达到数据最终一致。

而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:

  • AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致
  • CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。

分布式事务模型
解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。
在这里插入图片描述

三、Seata架构全景图

初识Seata
Seata 是一个开源的分布式事务解决方案,由阿里巴巴和蚂蚁金服共同开发,旨在提供高性能和易用的分布式事务服务。
官网地址https://seata.io/zh-cn/ 其中的文档、播客中提供了大量的使用说明、源码分析。

Seata事务管理中有三个核心角色:

  • TC (Transaction Coordinator) -事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。类比交通指挥中心
  • TM (Transaction Manager)-事务管理器:管理事务的范围和生命周期。开始全局事务、提交或回滚全局事务。类比项目总负责人
  • RM (Resource Manager) -资源管理器:管理分支事务的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。类比各部门执行人员
    在这里插入图片描述

Seata提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • AT模式:最终一致的分阶段事务模式, 无业务侵入,也是Seata的默认模式
  • SAGA模式:长事务模式,有业务侵入

四、实战演练

部署TC服务

参考资料:《seata的部署和集成》

  1. 准备数据库表
    在这里插入图片描述

  2. application.yml 做好seata的配置 注:tc也需要注册到nacos中
    在这里插入图片描述
    3. Docker部署
    需要注意,要确保nacos、mysql都在同一网络hm-net中。如果某个容器不再这个网络中,可以参考下面的命令将某容器加入指定网络:

docker network connect [网络名] [容器名]
在虚拟机的/root目录执行下面的命令:
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.200.101 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network hm-net \
-d \
seataio/seata-server:1.5.2

微服务集成Seata

  1. 首先,引入seata相关依赖:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <!--版本较低,1.3.0,因此排除-->
        <exclusion>
            <artifactId>seata-spring-boot-starter</artifactId>
            <groupId>io.seata</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--seata starter 采用1.4.2版本-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>
  1. 然后配置application.yml(所有参与同一个事务的微服务的都要配置), 让微服务通过注册中心找到seata-tc-server
seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址 #参考tc服务自己的registry.conf中的配置,包括:地址、namespace、group、 application-name 、cluster
     type: nacos
     nacos: # tc
        server-addr: 127.0.0.1:8848
        namespace : ""
        group: DEFAULT_GROUP
        application: seata-tc-server # tc服务在nacos中的服务名称
        username: nacos
        password: nacos
  tx-service-group: seata-demo #事务组,根据这个获取tc服务的cluster名称
  service:
    vgroup-mapping:  #事务组与TC服务cluster的映射关系
      seata-demo: WH

报错1:Caused by: java.lang.IllegalArgumentException: applicationId: null, txServiceGroup: null-seata-service-group
原因:target目录classes目录下没有application.yml文件
解决:maven需要clean后重新编译

报错2:io.seata.common.exception.FrameworkException: can not register RM,err:can not connect to services-server.
原因:网络连接桥段问题
解决:启动时带上主机号 seata-server.bat -p 8091 -h 192.168.200.1

动手实践-XA模式

XA模式原理
XA规范是x/Open组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。
在这里插入图片描述

XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务

实现XA模式
在这里插入图片描述

AT模式

AT模式原理
AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。
在这里插入图片描述
简述AT模式与XA模式最大的区别是什么?

  • XA模式一阶段不提交事务, 锁定资源;AT模式一阶段直接提交,不锁定资源。
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
  • XA模式强一致;AT模式最终一致

AT模式的脏写问题
在这里插入图片描述

AT模式的写隔离
在这里插入图片描述

非seata管理的全局事务发生异常情况
在这里插入图片描述

AT模式的优点

  • 一阶段完成直接提交事务,释放数据库资源,性能比较好
  • 利用全局锁实现读写隔离
  • 没有代码侵入,框架自动完成回滚和提交
    AT模式的缺点
  • 两阶段之间属于软状态,属于最终一致
  • 框架的快照功能会影响性能,但比XA模式要好很多

实现AT模式
在这里插入图片描述

undo_log表:
在这里插入图片描述

TCC模式

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

  • Try:资源的检测和预留;
  • Confirm: 完成资源操作业务;要求Try成功Confirm一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。
    在这里插入图片描述
    在这里插入图片描述

TCC的优点是什么?

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点是什么?

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理

实现TCC模式
在这里插入图片描述

@LocalTCC
public interface AccountTCCService {

    @TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")
    void dedect(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money") Integer money);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);

}

    @Override
    @Transactional
    public void dedect(String userId, Integer money) {
        //0.获取事务id
        String xid = RootContext.getXID();
        //0.1 判断freeze中是否有冻结记录,如果有,一定是cancel执行过,需要拒绝业务,避免业务悬挂
        AccountFreeze oldfreeze = freezeMapper.selectById(xid);
        if (oldfreeze != null){
            return;
        }
        //1.扣减可用余额
        accountMapper.deduct(userId, money);
        //2.记录冻结金额,事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freeze.setUserId(userId);
        freezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //根据事务id,删除冻结金额
        String xid = ctx.getXid();
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        String xid = ctx.getXid();
        AccountFreeze freeze = freezeMapper.selectById(xid);
        //空回滚的判断,判断freeze是否为null,为null证明try没执行,需要空回滚
        if (null == freeze){
            freeze = new AccountFreeze();
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            freeze.setUserId(ctx.getActionContext("userId").toString());
            freezeMapper.insert(freeze);
            return true;
        }
        //判断幂等 已经处理过一次calcel了,无需重复处理,避免多次执行回滚操作
        if (freeze.getState() == AccountFreeze.State.CANCEL){
            return true;
        }
        //1.恢复可用余额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        //2.将冻结金额清0,状态改为cancel
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}

SAGA模式

Saga模式是SEATA提供的长事务解决方案。也分为两个阶段:

  • 一阶段:直接提交本地事务
  • 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚

Saga模式优点

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高
  • 一阶段直接提交事务,无锁,性能好
  • 不用编写TCC中的三个阶段,实现简单

Saga模式 缺点

  • 软状态持续时间不确定,时效性差
  • 没有锁,没有事务隔离,会有脏写

4种模式对比与选型
在这里插入图片描述

五、常见坑点解决方案

1. 空回滚问题
现象:未执行Try却触发Cancel
解决:Cancel时检查冻结记录是否存在

public boolean cancel(BusinessActionContext ctx) {
    if(freezeMapper.selectById(xid) == null) {
        // 插入标记防止重复回滚
        freezeMapper.insertEmptyRollback(xid); 
        return true;
    }
    // 正常回滚逻辑...
}

2. 幂等控制
方案:数据库唯一索引 + 状态机检查

CREATE TABLE undo_log (
    id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    xid VARCHAR(100) NOT NULL UNIQUE,
    status TINYINT NOT NULL
);

3. TC集群部署

 # 多节点配置
seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata-cluster

六、性能优化建议

  1. 异步化:将非核心操作(如发短信)异步处理
  2. 批量提交:合并多个分支事务的TC请求
  3. 热点数据优化
@GlobalTransactional(timeoutMills = 60000)
public void batchProcess() {
    // 分批处理数据
    List<Data> partition = splitData();
    partition.parallelStream().forEach(this::processSingle);
}

七、监控体系搭建

Seata监控看板

  1. Metrics监控:集成Prometheus收集事务指标
  2. 日志追踪:通过xid串联全链路日志
  3. 告警设置
    • 事务成功率低于99.9%
    • 平均事务耗时超过500ms

八、总结

选型原则:

  • 强一致选XA,常规业务用AT,
  • 高并发用TCC,长流程选SAGA。

核心口诀:

  • TC协调全局事,TM注解定边界,
  • RM管资源,快照保回滚。

参考:黑马程序员视频笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值