其他分享
首页 > 其他分享> > 微服务:分步式事务TCCP

微服务:分步式事务TCCP

作者:互联网

分步式事务

TCC模式

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

流程

初识余额:

image-20210724182424907

余额充足,可以冻结:

image-20210724182457951

此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变。事务直接提交无需等待其它事务。

确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好了:

image-20210724182706011

此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70元


优点和缺点

TCC的优点是什么?

TCC的缺点是什么?


空回滚

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

业务悬挂

对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂


实现TCC

@LocalTCC
public interface AccountTCCService {
    @TwoPhaseBusinessAction(name = "deduct",commitMethod = "confirm",rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money") int money);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);
}
@Service
public class AccountTCCServiceImpl implements AccountTCCService {
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    /**
     * 这个是try方法,用来做扣减余额,
     * 和冻结余额并更改状态的
     * @param userId
     * @param money
     */
    @Override
    @Transactional
    public void deduct(String userId, int money) {

        //0.获取事务id
        String xid = RootContext.getXID();
        //0.1事务悬挂判断
        if(accountFreezeMapper.selectById(xid)!=null){
            return;
        }
        //1.扣减可用余额
        accountMapper.deduct(userId, money);
        //2.记录冻结余额,事务状态
        AccountFreeze accountFreeze = new AccountFreeze();
        accountFreeze.setUserId(userId);
        accountFreeze.setFreezeMoney(money);
        accountFreeze.setState(AccountFreeze.State.TRY);
        accountFreeze.setXid(xid);
        accountFreezeMapper.insert(accountFreeze);
    }

    /**
     * 这个是confirm方法,用来提交的。
     * 一旦不需要回滚则删除冻结记录。
     * @param ctx
     * @return
     */
    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //0.获取事务id
        String xid = ctx.getXid();
        //1.根据id删除冻结记录
        return accountFreezeMapper.deleteById(xid) == 1;

    }
    /**
     * 这个方法用来回滚。
     * 根据冻结记录回滚数据。
     * 进行了空回滚和多次执行的判断
     * @param ctx
     * @return
     */
    @Override
    public boolean cancel(BusinessActionContext ctx) {
        String xid = ctx.getXid();
        //0.查询冻结记录
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
        String userId = ctx.getActionContext("userId").toString();

        //0.1空回滚的判断
        if(accountFreeze==null){
            accountFreeze.setUserId(userId);
            accountFreeze.setFreezeMoney(0);
            accountFreeze.setState(AccountFreeze.State.CANCEL);
            accountFreeze.setXid(xid);
            return true;
        }
        //0.2幂处理判断
        if (accountFreeze.getState()== AccountFreeze.State.CANCEL){
            return true;
        }

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

Saga模式

Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。

其理论基础是Hector & Kenneth 在1987年发表的论文Sagas

原理

在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

image-20210724184846396

Saga也分为两个阶段:


优点和缺点

优点:

缺点:


四种模式对比

我们从以下几个方面来对比四种实现:

如图:

image-20210724185021819

标签:冻结,事务,xid,userId,ctx,TCCP,分步,accountFreeze
来源: https://www.cnblogs.com/Boerk/p/16101248.html