文章目录

  • 微服务框架
  • 分布式事务
  • 38 动手实践
  • 38.7 案例实现TCC 模式【分析】
  • 38.7.1 实现TCC 模式
  • 38.7.2 TCC 的空回滚 和业务悬挂
  • 38.7.3 业务分析


38 动手实践

38.7 案例实现TCC 模式【分析】
38.7.1 实现TCC 模式

【案例】改造account-service服务,利用TCC实现分布式事务

需求如下:

  • 修改account-service,编写try、confirm、cancel逻辑
  • try业务:添加冻结金额,扣减可用金额【这里需要再整一张表,作为冻结金额】
  • confirm业务:删除冻结金额
  • cancel业务:删除冻结金额,恢复可用金额
  • 保证confirm、cancel接口的幂等性 【幂等:接口不会因为重复调用而出现问题】
  • 允许空回滚
  • 拒绝业务悬挂

可以和AT 模式混着用

38.7.2 TCC 的空回滚 和业务悬挂

微服务 三层架构_微服务

这是上一节课老师讲过的TCC 事务模型,当前处于第一阶段,其中一个分支事务已经正常执行了

而另外一个分支事务也要去执行时,发现被阻塞了

微服务 三层架构_seata_02

等一段时间后【它不会无休止的等待】,它会报出超时错误

微服务 三层架构_架构_03

它会向TC 报一个超时的错误,说“不行, 这儿有人超时了,你还是回滚吧”

接着TC 一检查

微服务 三层架构_spring cloud_04

发现确实少了一个,就让它们都进行回滚

微服务 三层架构_微服务_05

RM 收到回滚要求后,就会开始进行cancel 逻辑

微服务 三层架构_微服务 三层架构_06

这里就出事儿了 ,第一个RM 执行cancel 没问题, 因为它已经try 了,但是第二个 RM 并没有执行过try, 但是现在却要它 进行cancel ,意思就是现在第二个RM 不能真的做一个cancel 回滚,只能做一个空的回滚,即空回滚

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

【业务悬挂】

现在第二个RM 已经执行了空 回滚了

就在这个时候,阻塞 畅通了

微服务 三层架构_微服务 三层架构_07

它就想着继续往下执行,它就去执行 try了,但是现在的全局事务已经结束了,不会再有第二阶段了

微服务 三层架构_微服务_08

即这个事务,只执行了try 逻辑,压根儿没有后续 了,这就是 业务悬挂

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

38.7.3 业务分析

为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:

微服务 三层架构_微服务 三层架构_09

CREATE TABLE `account_freeze_tbl` (
  `xid` varchar(128) NOT NULL,
  `user_id` varchar(255) DEFAULT NULL COMMENT '用户id',
  `freeze_money` int(11) unsigned DEFAULT '0' COMMENT '冻结金额',
  `state` int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

现在的各种业务:

  • try 业务:
  • 记录冻结金额和事务状态到account_freeze表
  • 扣减account表可用金额
  • confirm 业务
  • 根据xid删除account_freeze表的冻结记录
  • cancel 业务
  • 修改account_freeze表,冻结金额为0, state为2
  • 修改account表,恢复可用金额
  • 如何判断是否空回滚
  • cancel业务中,根据xid查询account_freeze,如果为null则说明try还没做,需要空回滚
  • 如何避免业务悬挂
  • try业务中,根据xid查询account_freeze,如果已经存在则证明Cancel已经执行,拒绝执行try业务