springcloud3 分布式事务解决方案seata之TCC模式6

一 TCC模式

1.1 TCC的逻辑

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC需要人工干预编写代码。需要实现三个方法:

  • Try:资源的检测和预留;

  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。

  • Cancel:预留资源释放,可以理解为try的反向操作。

1.2 流程图

TCC的执行流程可以分为两个阶段,分别如下:

(1)第一阶段:Try,业务系统做检测并预留资源 (加锁,锁住资源),比如常见的下单,在try阶段,我们不是真正的减库存,而是把下单的库存给锁定住。

(2)第二阶段:根据第一阶段的结果决定是执行confirm还是cancel

Confirm:执行真正的业务(执行业务,释放锁)

Cancle:是对Try阶段预留资源的释放(出问题,释放锁)

原文链接:https://blog.csdn.net/a745233700/article/details/122402303

1.3 优缺点

  • 一阶段完成直接提交事务,释放数据库资源,性能好

  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强

  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点是什么?

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦

  • 软状态,事务是最终一致

  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理

1.4 实现空回滚与悬挂

当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。

2.空悬挂

对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。

执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

二 利用TCC实现分布式事务的案例操作

2.1 工程结构

2.2 需求的描述

1.修改account-service,编写try,confirm,cancel逻辑;

2.try业务: 添加冻结金额,扣减可用金额;

3.confirm业务:删除冻结金额

4.cancel业务:删除冻结金额,恢复可用金额

2.3 解决空回滚和悬挂

**为了防止空回滚,业务悬挂,保证幂等性要求,需要新增一张表冻结表,在数据库记录冻结金额的同时,记录当前事务id和执行状态。**如下图所示:

2.4 附件数据库

2.5 编写try,confirm,cancel方法

1.在account-service模块,新增接口

复制代码
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;

@LocalTCC
public interface AccountTCCService {

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

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);
}

2.定义实现类

复制代码
package cn.itcast.account.service.impl;

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

@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    public void deduct(String userId, int money) {
        // 0.获取事务id
        String xid = RootContext.getXID();
        // 1.扣减可用余额
        accountMapper.deduct(userId, money);
        // 2.记录冻结金额,事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freezeMapper.insert(freeze);
    }

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

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        // 0.查询冻结记录
        String xid = ctx.getXid();
        AccountFreeze freeze = freezeMapper.selectById(xid);

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

3.修改controller调用

2.6 启动nacos,seata

1.启动nacos

2.启动seata

2.7 验证

2.7.1 初始态数据表

2.7.2 正常下单

1.请求: http://localhost:8082/order?userId=user202103032042012&commodityCode=100202003032041&count=1&money=50

2.各表数据

2.7.3 订单数大于库存数访问

1.请求:http://localhost:8082/order?userId=user202103032042012&commodityCode=100202003032041&count=10&money=50

2.查看控制台

account:

2.order

3.storage

3.查看数据表:回滚执行了cancel方法,冻结表新增一条冻结数据,其他表均进行了回滚。

相关推荐
LDG_AGI2 小时前
【推荐系统】深度学习训练框架(二十一):DistributedCheckPoint(DCP) — PyTorch分布式模型存储与加载
pytorch·分布式·深度学习
LDG_AGI2 小时前
【推荐系统】深度学习训练框架(二十三):TorchRec端到端超大规模模型分布式训练+推理实战
人工智能·分布式·深度学习·机器学习·数据挖掘·推荐算法
清晓粼溪2 小时前
SpringCloud-05-Micrometer Tracing+ZipKin分布式链路追踪
分布式·spring·spring cloud
独自破碎E2 小时前
聊聊RabbitMQ
分布式·rabbitmq
小股虫2 小时前
缓存攻防战:在增长中台设计一套高效且安全的缓存体系
java·分布式·安全·缓存·微服务·架构
2503_946971862 小时前
【FullStack/ZeroDay】2026年度全栈魔法架构与分布式恶意节点清除基准索引 (Benchmark Index)
分布式·网络安全·架构·系统架构·区块链·数据集·全栈开发
回家路上绕了弯2 小时前
Resilience4j全面指南:轻量级熔断限流框架的实战与落地
分布式·后端
LDG_AGI3 小时前
【推荐系统】深度学习训练框架(二十二):PyTorch2.5 + TorchRec1.0超大规模模型分布式推理实战
人工智能·分布式·深度学习
2503_946971863 小时前
【SystemDesign/HA】2025年度高可用分布式仿真节点与预测模型容灾演练配置 (Disaster Recovery Config)
大数据·分布式·算法·系统架构·数据集
linux修理工4 小时前
kafka topic consumer
分布式·kafka·linq