文章目录
- 微服务框架
- 分布式事务
- 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 事务模型,当前处于第一阶段,其中一个分支事务已经正常执行了
而另外一个分支事务也要去执行时,发现被阻塞了
等一段时间后【它不会无休止的等待】,它会报出超时错误
它会向TC 报一个超时的错误,说“不行, 这儿有人超时了,你还是回滚吧”
接着TC 一检查
发现确实少了一个,就让它们都进行回滚
RM 收到回滚要求后,就会开始进行cancel 逻辑
这里就出事儿了 ,第一个RM 执行cancel 没问题, 因为它已经try 了,但是第二个 RM 并没有执行过try, 但是现在却要它 进行cancel ,意思就是现在第二个RM 不能真的做一个cancel 回滚,只能做一个空的回滚,即空回滚
∴ 当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
【业务悬挂】
现在第二个RM 已经执行了空 回滚了
就在这个时候,阻塞 畅通了
它就想着继续往下执行,它就去执行 try了,但是现在的全局事务已经结束了,不会再有第二阶段了
即这个事务,只执行了try 逻辑,压根儿没有后续 了,这就是 业务悬挂
∴ 对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂
38.7.3 业务分析
为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:
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业务