TCC事务介绍
TCC(Try-Confirm-Cancel)
是除可靠消息队列以外的另一种常见的分布式事务机制,它是由数据库专家帕特 · 赫兰德(Pat Helland
)在2007年撰写的论文《Life beyond Distributed Transactions: An Apostate’s Opinion》中提出的。正式以Try-Confirm-Cancel
作为名称的是Atomikos公司,其注册了TCC商标。
Atomikos公司在商业版本事务管理器ExtremeTransactions中提供了TCC方案的实现,但是由于其是收费的,因此相应的很多的开源实现方案也就涌现出来,如:tcc-transaction
、ByteTCC
、hmily
、spring-cloud-rest-tcc
。
上次我们分享的使用RocketMQ实现分布式事务,虽然它也能保证最终的结果是相对可靠的,过程也足够简单(相对于TCC来说),但可靠消息队列的整个实现过程完全没有任何隔离性可言。
如果业务需要隔离,我们通常就应该重点考虑TCC方案,它天生适合用于需要强隔离性的分布式事务中。
在具体实现上,TCC的操作有点复杂,它是一种业务侵入性较强的事务方案,要求业务处理过程必须拆分为“预留业务资源”和“确认/释放消费资源”两个子过程。另外,你看名字也能看出来,TCC的实现过程分为了三个阶段:
• Try: 尝试执行阶段,完成所有业务可执行性的检查(保障一致性),并且预留好事务需要用到的所有业务资源(保障隔离性)。
-
Confirm: 确认执行阶段,不进行任何业务检查,直接使用Try阶段准备的资源来完成业务处理。注意,
Confirm
阶段可能会重复执行,因此需要满足幂等性。 -
Cancel: 取消执行阶段,释放Try阶段预留的业务资源。注意,
Cancel
阶段也可能会重复执行,因此也需要满足幂等性。
空回滚和业务悬挂问题
空回滚:当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
业务悬挂:对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂。
TCC与XA区别
之前分布式事务-两阶段、三阶段提交介绍了二阶段提交,TCC与XA两阶段提交有着异曲同工之妙。下面我们看下两者的区别:
-
执行阶段上:
-
阶段1: 在XA中,各个RM准备提交各自的事务分支,事实上就是准备提交资源的更新操作(insert、delete、update等);而在TCC中,是主业务活动请求(try)各个从业务服务预留资源。
-
阶段2: XA根据第一阶段每个RM是否都prepare成功,判断是要提交还是回滚。如果都prepare成功,那么就commit每个事务分支,反之则rollback每个事务分支。TCC中,如果在第一阶段所有业务资源都预留成功,那么confirm各个从业务服务,否则取消(cancel)所有从业务服务的资源预留请求。
-
-
XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁 XA事务中的两阶段提交内部过程是对开发者屏蔽的,JTA规范中,通过
UserTransaction
的commit
方法来提交全局事务,这只是一次方法调用,其内部会委派给TransactionManager
进行真正的两阶段提交,因此开发者从代码层面是感知不到这个过程的。而事务管理器在两阶段提交过程中,从prepare
到commit/rollback
过程中,资源实际上一直都是被加锁的。如果有其他人需要更新这两条记录,那么就必须等待锁释放。 -
TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁 TCC中的两阶段提交并没有对开发者完全屏蔽,也就是说从代码层面,开发者是可以感受到两阶段提交的存在。如下单案例中:在第一阶段,库存中心需要提供try接口(库存预留)。在第二阶段,库存需要提供
confirm/cancel
接口(确认库存扣减/取消库存预扣减)。开发者明显的感知到了两阶段提交过程的存在。try、confirm/cancel
在执行过程中,一般都会开启各自的本地事务,来保证方法内部业务逻辑的ACID特性。其中:-
1.
try
过程的本地事务,是保证资源预留的业务逻辑的正确性。 -
2.
confirm/cancel
执行的本地事务逻辑确认/取消预留资源,以保证最终一致性,也就是所谓的补偿型事务(Compensation-Based Transactions
)。
-
示例
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:资金服务、红包服务、订单服务。
红包服务
public interface RedPacketTradeOrderService {
@EnableTcc
public String record(RedPacketTradeOrderDto tradeOrderDto);
}
资金服务
public interface CapitalTradeOrderService {
@EnableTcc
public String record(CapitalTradeOrderDto tradeOrderDto);
}
订单服务
主要业务逻辑如下:
@Service
public class PaymentServiceImpl {
@Autowired
CapitalTradeOrderService capitalTradeOrderService;
@Autowired
RedPacketTradeOrderService redPacketTradeOrderService;
@Autowired
OrderRepository orderRepository;
@Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = false)
public void makePayment(@UniqueIdentity String orderNo) {
System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order order = orderRepository.findByMerchantOrderNo(orderNo);
String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
}
public void confirmMakePayment(String orderNo) {
System.out.println("order confirm make payment called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);
//check if the trade order status is PAYING, if no, means another call confirmMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null) {
foundOrder.confirm();
orderRepository.update(foundOrder);
}
}
public void cancelMakePayment(String orderNo) {
System.out.println("order cancel make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);
//check if the trade order status is PAYING, if no, means another call cancelMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null) {
foundOrder.cancelPayment();
orderRepository.update(foundOrder);
}
}
}
相关资料
开源TCC实现方案
-
https://github.com/changmingxie/tcc-transaction
-
https://github.com/liuyangming/ByteTCC
-
https://github.com/dromara/hmily
-
https://github.com/prontera/spring-cloud-rest-tcc
Atomikos的官方TCC参考文档
-
https://www.atomikos.com/downloads/articles/TransactionsForSOA-WhitePaper.pdf
-
https://www.atomikos.com/Main/DownloadPublications?article=TccForRestApi.pdf
-
Atomikos ExtremeTransactions Guide:atomikos 商业版本事务管理器ExtremeTransactions使用指南。