时间:26-04-24
为分布式事务选择一致性模型,首要任务是审视业务场景:你的业务逻辑,是否必须强一致,还是能够容忍最终一致?
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
要做出准确判断,必须清晰理解两者的本质区别。
核心的判别标准非常聚焦:在整个事务的生命周期内,是否存在任何不符合业务语义的“中间态”数据被暴露。
这个概念听起来抽象,我们用一个典型场景来剖析。以电商领域的“扣减库存-创建订单”流程为例。
业务逻辑是线性的:库存服务完成扣减后,再调用订单服务生成订单。那么,如何界定其一致性?
业务期望的最终状态只有两种:库存成功扣减且订单创建完成;或者库存未动且订单也未生成。问题的关键在于,是否会出现第三种“矛盾”状态:库存已扣但订单缺失;或库存未扣但订单已生成。这两种,就是典型的非预期中间状态。
接下来,我们将依据这一标准,深入对比Seata的AT模式与TCC模式,分析它们各自产生的数据状态。
通过下面的时序图,我们可以清晰地梳理其执行脉络:
在步骤8,库存服务的本地数据库事务就已提交。请注意,此时全局事务尚未提交,订单服务甚至还未被调用。这就引入了一个潜在问题:若此时有其他并发查询请求读取该商品库存,它将读到什么?答案是已被扣减后的数量。然而,与之对应的订单数据却根本不存在。这读取到的,正是非业务预期的中间状态数据。
当然,如果后续订单服务调用成功,订单顺利生成,这个问题就被掩盖了,数据最终达成一致。但若订单服务失败,全局事务将回滚,库存数量会被恢复。然而,之前那个读到“已扣减库存”的查询,获取的已然是一份“脏数据”。在此场景下,AT模式提供的是“最终一致性”保障。
本质上,Seata AT模式是对传统两阶段提交(2PC)协议的一种优化。优化的核心在于分支事务的提交时机。经典2PC要求所有分支事务在准备阶段完成后均等待,直到协调者发出提交指令才一并提交本地事务,这导致数据库锁持有时间过长,影响性能。AT模式的改进在于:分支事务在完成本地操作后立即提交,释放数据库锁,从而换取更高的并发吞吐量。
一个关键细节在于:库存服务与订单服务持有的全局锁,在正常提交与异常回滚两种路径下的释放时机不同,且释放速度存在本质差异。这正是AT模式在高成功率业务场景下性能表现优异的核心机制。
这也解释了为何在业务成功率极高(例如超过99%)的场景中,Seata AT模式能够展现出卓越的性能。
此外,需要考虑一个极端场景。在AT模式中,若第一阶段所有分支均报告成功,事务管理器发起全局提交,但进入第二阶段后,订单服务在提交时失败(可能由于网络闪断、服务宕机或数据库异常)。这属于“提交阶段异常”。
根据2PC及Seata的设计原则,一旦事务管理器决定提交(进入第二阶段),所有分支事务必须最终提交,不可再回滚。那么,第二阶段某个分支提交失败如何处理?事务协调器会负责持续重试,直至成功。这通常依赖于协调器内部的重试队列或定时任务来保证。由于第一阶段已通过业务校验并锁定资源,理论上第二阶段的提交操作(主要是释放锁、清理日志)最终必然成功。
TCC模式与Seata AT最根本的区别在于:它不依赖于数据库原生的ACID事务与锁机制,而是要求开发者通过业务代码,显式地实现资源的预留、确认与取消。
首先分析Try阶段(这是整个模式的核心):
对于库存服务,并非直接扣减,而是执行“冻结”操作。例如商品总库存100件,Try阶段冻结1件,可用库存显示为99件。注意,这1件并未被实际消耗,若后续流程失败,它可以被“解冻”归还。对于订单服务,也并非直接生成有效订单,而是创建一个处于“中间态”(如“待确认”)的订单记录,或仅预占一个订单号。此阶段的“锁”,是业务逻辑层面的锁(例如通过一个`frozen`字段标记),而非数据库的排他锁。这意味着其他事务仍可操作这行数据(只要不触及已冻结部分),从而实现了更高的并发度。
再看Confirm阶段:
此阶段仅执行确认操作。由于资源已在Try阶段预留妥当,Confirm阶段理论上只应成功,不应失败(除非遭遇数据库宕机等极端故障)。同时,Confirm操作的代码必须具备幂等性,以应对网络抖动导致的重复调用。
最后是Cancel阶段(图中未展示,但其逻辑与Confirm相反):
如果Try阶段任一环节失败,事务协调器将调用各服务的Cancel方法。库存服务会将冻结库存加回可用库存(解冻);订单服务则会将“待确认”订单删除或标记为“已取消”。
那么,为何说TCC模式能提供强一致性?关键在于其对“中间状态”的业务定义。
以Try阶段为例,参照上图。在库存服务执行完`tryDeductStock`(步骤6)并提交本地事务后,若有其他线程查询商品库存,它将看到的状态是:【总库存不变,但可用库存-1,冻结库存+1】。
请注意,这虽然是业务流程中的一个中间状态,但它完全符合业务预期,不存在任何歧义。无论后续流程走向如何——是订单创建失败导致库存解冻,还是订单创建成功导致冻结库存被正式消耗——这些状态变迁都严格遵循预先定义的业务语义。因此,TCC模式实现了数据的强一致性。
必须强调:TCC所能提供的强一致性,高度依赖于业务状态定义的准确性与无二义性。如果在Try阶段完成后,数据状态本身就存在逻辑矛盾,那么这已非TCC模式能够解决的问题,而需要在业务架构设计层面进行修正。