还不懂分布式事务:带你深入剖析TCC实现原理

TCC概念

常见的分布式事务实现方案有以下几种:两阶段提交(2PC)、三阶段提交(3PC)、TCC(Try-Confirm-Cancel)、补偿事务(Saga)、MQ事务消息等。

由于2PC资源锁定时间较长,性能较差,难以扩展。TCC旨在解决这些问题,TCC是一种补偿型事务模式,通过将每个事务操作分为三个阶段:尝试(Try)、确认(Confirm)和取消(Cancel),不仅优化了事务的执行流程,还提高了系统的整体效率和弹性。

TCC流程

TCC的核心思想在于将事务的每个操作拆分为三个明确的阶段:尝试(Try)、确认(Confirm)和取消(Cancel)。这三个阶段各自承担不同的职责,确保事务在分布式环境中能够安全且一致地执行。

  • Try 阶段

在这个阶段,事务参与者将执行所有必要的检查,并预留必需的资源来保证事务可以顺利完成。例如,在电商场景,当用户尝试购买商品时,系统将在Try阶段检查商品库存,预留所需数量的商品。这个阶段是整个TCC流程中的准备步骤,它不会做任何实际的业务处理,只是确保后续的Confirm阶段可以无障碍地执行。

  • Confirm 阶段

只有当所有参与者的Try阶段成功完成后,才会进入Confirm阶段。在这个阶段,系统将正式执行业务操作,使用在Try阶段预留的资源。继续举例电商场景,如果用户的支付成功,系统将在Confirm阶段正式从库存中扣除商品,完成交易。这一步骤确保了事务的最终一致性和数据的正确性。

  • Cancel 阶段

如果在Try阶段或Confirm阶段中的任何一个环节发生错误,或者某些业务条件未得到满足,整个事务将进入Cancel阶段。在这个阶段,所有已经预留的资源将被释放,所有在Try阶段所做的准备工作将被撤销。例如,如果用户决定取消购买,或者支付未成功,预留的商品库存将被释放,以便其他用户购买。

电商场景介绍

举一个常见的电商场景,用户购买商品,点击支付后。这时候交易系统需要做的操作是:

  1. 更新订单状态为“已支付”
  2. 扣减库存
  3. 增加用户积分

image.png

电商系统一般采用微服务架构,上述三个操作分别需要调用三个服务完成。流程如下:

  1. 订单服务,更新订单状态为“已支付”
  2. 库存服务,扣减库存
  3. 积分服务,增加用户积分

如果这三个操作在一个服务中,就可以使用本地事务保证订单、库存、积分数据的一致性,但是现在需要调用三个服务,就无法保证数据的一致性了。 如果更新订单状态为“已支付”成功了,但是扣减库存失败了,其他用户就可以继续购买商品,可能会出现超卖问题,这时候就需要引入分布式事务,用来保证分布式系统中数据的一致性。

TCC落地电商场景

把上述电商场景引入TCC事务之后,就变成以下情况。

  • Try阶段实现

Try阶段负责资源的预检查和预留,分别调用三个服务冻结资源:

  1. 订单服务,更新订单状态为“支付中”
  2. 库存服务,冻结库存(库存表中,新增一个冻结库存的字段)
  3. 积分服务,预增加用户积分(积分表,新增一个预增加积分的字段)

image.png

库存表中,新增了一个冻结库存的字段。在商品详情页面展示剩余库存数量的时候,需要减去冻结库存,防止超卖。

  • Confirm 阶段

当Try阶段都执行成功的时候,就进入Confirm阶段,负责确认资源。流程如下:

  1. 订单服务,更新订单状态为“已支付”
  2. 库存服务,把冻结库存扣减到剩余库存上(update 剩余库存=剩余库存-冻结库存,冻结库存=0)
  3. 积分服务,把预增加积分加到总积分上(udpate 总积分=总积分+预增加积分,预增加积分=0)

image.png

  • Cancel阶段

当Try阶段任何一个操作失败,就进入Cancel阶段,负责回滚资源。流程如下:

  1. 订单服务,更新订单状态为“已取消”
  2. 库存服务,释放冻结的库存
  3. 积分服务,释放预增加积分

image.png

TCC问题

上述只是理想情况,在实践的过程中会遇到以下三大问题,影响TCC事务的安全性。

  1. Confirm/Cancel操作失败问题
  2. 空回滚问题
  3. 悬挂问题

针对每个问题,我们思考一下相应的解决方案。

1. Confirm/Cancel操作失败问题

在执行完Try阶段之后,进入到Confirm或者Cancel阶段,可能由于服务问题或者网络问题,Confirm或者Cancel操作不一定能成功,通常采用的办法是不断重试,要求Confirm和Cancel接口要支持幂等。

2. 空回滚问题

空回滚问题是指Try阶段某个服务的Try操作没有执行成功或者没有执行,进入Cancel阶段,就执行Cancel操作回滚资源,导致数据错乱。 常用的解决方案是Cancel操作前增加事务状态检查。

  1. 在 Try 操作开始时,设置事务状态为 TRYING。
  2. 如果 Try 操作成功,更新状态为 TRIED。
  3. 在 Cancel 操作执行前,检查状态。如果状态不是 TRIED,就不执行 Cancel 操作。

3. 悬挂问题

悬挂问题是指 Cancel 操作比 Try 操作先完成。通常出现的原因是由于网络延迟,例如:由于调用Try操作耗时较长,出现网络超时,导致Try操作调用失败,就进入Cancel阶段,执行Cancel操作,而此时Try操作还在执行,最后结果是Cancel操作执行成功后,Try操作又执行成功,出现“悬挂”问题。

解决方案有以下几种:

  1. 与空回滚解决方案类似,Cancel操作前增加事务状态检查。
    1. 在 Try 操作开始时,设置事务状态为 TRYING。
    2. 如果 Try 操作成功,更新状态为 TRIED。
    3. 在 Cancel 操作执行前,检查状态。如果状态不是 TRIED,就不执行 Cancel 操作或者延迟执行。
  2. Try操作引入重试机制

如果调用Try操作超时,可以进行有限次重试,而不是立即执行Cancel操作,可以减少因为网络超时而导致的悬挂问题。这里要求Try接口也要支持幂等操作。

  1. 增加同步机制

可以使用分布式锁来控制Try和Cancel操作的执行顺序。

总结

TCC模型将事务分为Try、Confirm和Cancel三个阶段,使得事务处理更加灵活和可控,保证了数据的一致性,减少了2PC资源锁定时间过长的问题。但是也引入了一些新问题:

  1. 代码侵入严重。Try、Confirm和Cancel三个阶段的事务操作都要耦合在业务逻辑中,耦合性较高。
  2. 设计复杂,开发成本较高。对于TCC引入的幂等操作、空回滚问题、悬挂问题,都需要架构设计的时候考虑相应的解决方案。

需要开发人员自行评估使用成本。

  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值