柔性事务TCC的分析
前言
相信搜到这篇文章的同学都知道TCC的大概由来,就不累述了。简单说:为了解决微服务所带来的事务问题而产生的一种设计思想。本文不重复和copy一些理论概念,只从本人的理解出发,从一个微服务爱好者的角度出发,分析这种设计思想。
基础概念
TCC : try,confirm,cancel,就那么简单的三个字。
try: 进行业务的检查和预操作。
confirm: 将try中的预操作落实,进行的确认操作。
cancel: 如果try失败了,进行回滚,也算是一种补偿处理。
如果对tcc不是特别了解的同学,看到上面其实还是一篇模糊,不知道这个框架到底想干什么。下面我们具体实例看下,他是怎么应用的。
实例理解
实例:用大家最容易理解的下单功能来讲。用户下单后,要执行3个内容:
- 生成订单-OrderService
- 扣减库存-StockService
- 生成会员积分-MembershipService
我们假设这三个service都是独立的三个微服务。
我们分成三个阶段来看:
Try:
OrderService:生成订单,并给订单一个初始状态:pending
StockService:冻结库存(预操作)。库存一般两个字段:可用库存数,冻结库存 。下单前可用库存数 为10,下单1个产品,那么这一步的结果就是:可用库存数:9,冻结库存:1
MembershipService: 预发积分(预操作)。原理和库存一样,也是两个字段:可用积分,冻结积分。原始积分:10。这一步的结果:可用积分:10,冻结积分:1
注意: 这一步其中也包含了基本的业务检查:比如,检查库存数量是否够,会员是否激活等。如果基础检查失败,会直接调用cancel步骤。
Confirm
OrderService:更新订单状态:confirmed
StockService:扣减库存:这一步的结果就是:可用库存数:9,冻结库存:0
MembershipService:发放积分:这一步的结果:可用积分:11
Cancel
OrderService:更新订单状态:cancel
StockService:还原库存:这一步的结果就是:可用库存数:10,冻结库存:0
MembershipService:移除预发积分:这一步的结果:可用积分:10
调用顺序说明:
先Try,所有try中的service都执行成功后,会调用Confirm。如果Try中有一个执行失败,就会调用对应的Cancel来进行取消,保持数据的一致性。
优劣
还是老生常谈的谈谈我认为的这个方案的优劣吧
优势:
- 性能优于XA,不会像两段式事务锁定整个资源。
- 最终一致,通过补偿手段最终可以保证数据一致
缺点:
我主要觉得: 业务代码需要大量调整,数据模型要支持预操作,要增加不少的业务代码。
开源框架
tcc-transaction,Byte-tcc 等开源框架。
问题讨论
1. Confirm失败了如何处理?
因为Try中进行了业务检查,Confirm阶段业务性失败的概率很低,除非有bug。另外一个导致失败的原因可能就是服务down了,微服务一般都是HA,所以服务down的概率也很低。即便如此 一般框架还是会预防这种情况,会提供重试机制,当confirm失败后,会进行重试,虽然不算完美,但是也算一个解决思路。
2. 是否支持异步?
关于异步的分布式事务,我个人更倾向于使用RocketMQ这种方案,Rocket本身也支持分布式事务。
最后
TCC是否能解决你当前的问题,我觉得真不一定,方案没有好坏,只有是否适合,你还是需要匹配你的项目周期,人员能力等综合因素来选择方案。后续有时间,我也会更新其他分布式事务的介绍,感兴趣的朋友,可以关注下。
