在分布式系统设计中,随着微服务的流行,通常一个业务操作被拆分为多个子任务,比如电商系统的下单和支付操作,就涉及到了创建和更新订单、扣减账户余额、扣减库存、发送物流消息等,那么在复杂业务开发中,如何保证最终数据一致性呢?
一、TCC 事务模型简介
TCC 事务模型将事务的执行过程分为三个阶段:Try、Confirm 和 Cancel。
- Try 阶段:尝试执行事务,完成所有业务检查(一致性),预留必须的业务资源(准隔离性)。
- Confirm 阶段:确认执行真正的事务操作,不做任何业务检查,只使用 Try 阶段预留的业务资源。此阶段必须保证事务的原子性和持久性。
- Cancel 阶段:取消执行事务,释放 Try 阶段预留的业务资源。此阶段必须保证事务的原子性和幂等性。
二、实际场景分析
假设我们有一个电商系统,其中包含订单服务、库存服务和支付服务。当用户下单时,需要同时更新订单状态、扣减库存和进行支付。如果其中任何一个环节出现问题,都需要回滚整个事务。下面我们将以这个场景为例,分析如何在业务中体现 TCC 事务模型。
(一)Try 阶段
- 订单服务:创建一个新的订单,并将订单状态设置为“待支付”。同时,记录下订单的相关信息,如商品数量、总价等。
- 库存服务:检查库存是否足够,如果足够,则预留相应数量的库存。可以通过在库存表中增加一个预留字段来表示预留的库存数量。
- 支付服务:检查用户的支付账户是否有足够的余额,如果足够,则预留相应的金额。可以通过在支付表中增加一个预留字段来表示预留的金额。
(二)Confirm 阶段
- 订单服务:如果所有服务的 Try 阶段都成功,则将订单状态设置为“已支付”,并通知用户订单已成功支付。
- 库存服务:确认扣减预留的库存数量,将商品从库存中移除。
- 支付服务:确认扣除预留的金额,完成支付操作。
(三)Cancel 阶段
- 订单服务:如果任何一个服务的 Try 阶段失败,则将订单状态设置为“已取消”,并通知用户订单已被取消。
- 库存服务:如果 Try 阶段成功但 Confirm 阶段失败,则释放预留的库存数量,将预留字段的值恢复为 0。
- 支付服务:如果 Try 阶段成功但 Confirm 阶段失败,则释放预留的金额,将预留字段的值恢复为 0。
三、TCC 事务模型的优势
- 性能高:TCC 事务模型可以在业务层面进行控制,避免了传统事务处理方式中的锁竞争和阻塞,提高了系统的性能。
- 灵活性强:TCC 事务模型可以根据业务需求进行灵活的调整和扩展,适用于各种复杂的业务场景。
- 可靠性高:TCC 事务模型通过 Try、Confirm 和 Cancel 三个阶段的设计,保证了事务的原子性、一致性和持久性,提高了系统的可靠性。
四、TCC 事务模型适用的其他场景
(一)金融系统
在金融交易中,需要保证交易的原子性和一致性。例如,在银行转账业务中,从一个账户扣款并向另一个账户入账,如果其中任何一个操作失败,都需要回滚整个交易。TCC 事务模型可以很好地应用于这种场景,通过 Try 阶段检查账户余额是否足够,预留扣款金额;Confirm 阶段完成真正的扣款和入账操作;Cancel 阶段在出现问题时回滚交易,释放预留的金额。
(二)预订系统
在酒店、机票等预订系统中,用户预订后需要锁定资源(如房间、座位),并进行支付。如果支付失败或用户取消预订,需要释放锁定的资源。TCC 事务模型可以用于实现这种预订业务,Try 阶段锁定资源并检查用户的支付状态;Confirm 阶段完成支付并确认预订;Cancel 阶段在出现问题时释放锁定的资源。
(三)物流系统
在物流配送中,可能涉及多个环节,如发货、运输、签收等。如果其中任何一个环节出现问题,都需要回滚整个物流流程。TCC 事务模型可以应用于物流系统,Try 阶段记录物流状态并预留相关资源;Confirm 阶段完成各个环节的操作;Cancel 阶段在出现问题时回滚物流流程,释放预留的资源。
五、保证事务一致性
(一)Try 阶段的业务检查
在 Try 阶段,各个参与事务的服务进行业务检查,确保事务可以顺利进行。例如,在电商系统中,订单服务在 Try 阶段检查商品库存是否足够、用户账户是否正常等;库存服务检查库存数量是否满足订单需求;支付服务检查用户支付账户余额是否充足。通过这些业务检查,可以提前发现可能导致事务不一致的问题,从而避免在后续阶段出现错误。
(二)Confirm 和 Cancel 阶段的补偿操作
如果事务在 Try 阶段成功,但是在 Confirm 阶段出现问题,或者事务在 Try 阶段就失败了,那么就需要进入 Cancel 阶段进行补偿操作。Cancel 阶段的目的是释放 Try 阶段预留的资源,以保证事务的一致性。例如,在电商系统中,如果支付服务在 Confirm 阶段失败,那么订单服务和库存服务就需要进入 Cancel 阶段,将订单状态改为“已取消”,释放库存服务预留的库存。这样可以确保系统状态不会出现不一致的情况。
六、保证事务原子性
(一)Confirm 阶段的原子性
在 Confirm 阶段,必须保证事务的原子性,即要么所有的操作都成功,要么都失败。为了实现这一点,可以采用多种技术手段。例如,可以使用数据库事务来保证 Confirm 阶段的操作要么全部成功提交,要么全部回滚。如果 Confirm 阶段的操作涉及多个服务,可以使用分布式事务框架来协调各个服务的操作,确保它们要么全部成功,要么全部回滚。
(二)Cancel 阶段的原子性和幂等性
Cancel 阶段也必须保证事务的原子性,即释放 Try 阶段预留的资源必须全部成功。同时,Cancel 阶段还需要保证幂等性,即多次执行 Cancel 操作的结果应该是一致的。这是因为在分布式系统中,可能会出现网络故障等问题,导致 Cancel 操作需要重复执行。为了实现 Cancel 阶段的原子性和幂等性,可以使用数据库事务或者分布式事务框架来协调各个服务的操作,并且在执行 Cancel 操作时,记录操作的状态,以便在出现重复执行的情况下,可以判断是否已经执行过 Cancel 操作。
四、实际场景示例
以电商系统为例,假设用户下单购买商品,涉及订单服务、库存服务和支付服务。
(一)Try 阶段
- 订单服务:创建一个新的订单,并将订单状态设置为“待支付”。同时,记录下订单的相关信息,如商品数量、总价等。
- 库存服务:检查库存是否足够,如果足够,则预留相应数量的库存。可以通过在库存表中增加一个预留字段来表示预留的库存数量。
- 支付服务:检查用户的支付账户是否有足够的余额,如果足够,则预留相应的金额。可以通过在支付表中增加一个预留字段来表示预留的金额。
(二)Confirm 阶段
- 订单服务:如果所有服务的 Try 阶段都成功,则将订单状态设置为“已支付”,并通知用户订单已成功支付。
- 库存服务:确认扣减预留的库存数量,将商品从库存中移除。
- 支付服务:确认扣除预留的金额,完成支付操作。
(三)Cancel 阶段
- 订单服务:如果任何一个服务的 Try 阶段失败,则将订单状态设置为“已取消”,并通知用户订单已被取消。
- 库存服务:如果 Try 阶段成功但 Confirm 阶段失败,则释放预留的库存数量,将预留字段的值恢复为 0。
- 支付服务:如果 Try 阶段成功但 Confirm 阶段失败,则释放预留的金额,将预留字段的值恢复为 0。
在这个例子中,通过 TCC 事务模型的三个阶段,可以保证在用户下单购买商品的过程中,事务的一致性和原子性。如果任何一个环节出现问题,都可以通过 Cancel 阶段进行补偿操作,确保系统状态不会出现不一致的情况。同时,Confirm 阶段的原子性保证了事务的最终结果要么是全部成功,要么是全部失败。
TCC 事务模型通过 Try、Confirm 和 Cancel 三个阶段的设计,在实际场景中可以有效地保证事务的一致性和原子性。在 Try 阶段进行业务检查,提前发现可能导致事务不一致的问题;在 Confirm 和 Cancel 阶段进行补偿操作,确保系统状态的一致性。同时,通过使用数据库事务或分布式事务框架,可以保证 Confirm 和 Cancel 阶段的原子性和幂等性。在实际应用中,需要根据具体的业务需求和系统架构,合理地设计和实现 TCC 事务模型,以确保分布式系统中的事务能够正确地执行。
文章(专栏)将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发。
个人小工具程序上线啦,通过公众号(服务端技术精选)菜单【个人工具】即可体验,欢迎大家体验后提出优化意见!