在分布式系统中,不同服务需要协同完成一项业务操作,如跨服务订单创建、支付、库存扣减等。为了保证在分布式环境下的数据一致性,分布式事务的概念被引入。本文将深入探讨分布式事务的角色、机制,以及为什么由 TM(事务管理器)而非 TC(事务协调器)来决定事务提交或回滚。
分布式事务中的角色
分布式事务通常涉及三个关键角色:
-
TM(事务管理器)
- 执行业务逻辑。
- 决定全局事务是提交还是回滚。
-
TC(事务协调器)
- 管理全局事务的状态。
- 协调分支事务的提交或回滚。
-
RM(资源管理器)
- 管理本地资源(如数据库、消息队列等)。
- 执行具体的本地事务操作(如提交、回滚),并将执行结果反馈给 TC。
分布式事务的基本流程
-
TM 向 TC 申请开启全局事务
- TM 负责发起分布式事务,并向 TC 请求开启一个全局事务。
- TC 创建全局事务并生成一个唯一的事务 ID(XID)。
-
XID 在微服务调用链路中传播
- XID 作为事务的标识,会随着微服务调用链路传播。
- 每个微服务的分支事务都会关联到这个全局事务 ID。
-
RM 向 TC 注册分支事务
- 各微服务的 RM 向 TC 注册自己的分支事务,并将其纳入全局事务的管辖范围。
-
TM 决定提交或回滚
- TM 根据业务逻辑的执行情况,判断事务是否成功。
- TM 向 TC 发起全局事务的提交或回滚请求。
-
TC 协调分支事务的执行
- TC 根据 TM 的决议,通知所有注册的 RM 执行提交或回滚操作。
- RM 执行本地事务并将结果返回给 TC。
为什么是 TM 决定事务提交或回滚,而不是 TC?
1. TC 不理解业务语义
- TC 的职责是协调分支事务的提交或回滚,确保所有操作按照全局事务的决议一致进行。
- TM 的职责是执行业务逻辑,掌握业务语义。
- 即使 TC 接收到所有分支事务的状态(如成功或失败),它无法判断这些状态是否满足业务逻辑的要求。
示例:
假设订单事务包含以下步骤:
- 库存服务:扣减库存 → 成功
- 支付服务:扣款 → 失败
- 订单服务:生成订单 → 未执行
TC 只知道支付失败,但它无法判断支付失败是否可以通过重试解决,或者支付失败是否意味着整个业务必须回滚。这需要 TM 根据业务逻辑做出判断。
2. 职责分离原则
- 分布式事务的设计遵循职责分离原则,让各组件各司其职:
- TM:负责执行业务逻辑,决定事务的最终状态。
- TC:负责管理和调度事务,执行 TM 的决议。
- RM:执行本地事务,汇报执行结果。
- 如果 TC 直接决定事务的提交或回滚,就需要理解业务逻辑,增加了系统复杂性,破坏了事务与业务的解耦。
3. TC 决策可能导致错误判断
- TC 只能根据 RM 汇报的状态(如成功或失败)进行判断,但这些状态并不一定能准确反映实际情况。
- 示例:
- 支付服务的 RM 返回“失败”,但失败可能是由于网络抖动导致的,此时支付实际上是成功的。
- 如果 TC 根据“失败”直接决定回滚,则可能导致订单取消但支付已经成功的错误情况。
- TM 作为业务逻辑的执行者,可以根据规则决定是否重试、补偿,或者直接回滚。
4. TM 拥有全局业务视角
- TM 掌握全局业务状态,了解所有分支事务的依赖关系及执行顺序。
- TC 只负责事务的执行状态,它不知道这些状态如何映射到业务逻辑上。
- 例如,在复杂的场景中,某些服务的失败可以通过重试解决,而有些则不能,这种判断只能由 TM 来完成。
分布式事务角色间的交互关系
以下是一个典型的分布式事务的交互过程:
TM(事务管理器)
|
|—— 向 TC 申请开启全局事务,TC 生成 XID
|
|—— 调用各服务的分支事务
|—— RM 向 TC 注册分支事务
|—— RM 向 TC 汇报分支事务的执行结果
|
|—— 收集所有分支事务状态,做出提交或回滚决策
|
|—— 向 TC 发起全局事务的提交或回滚
|
|—— TC 通知所有 RM 执行提交或回滚操作
总结
在分布式事务中,TM 发起提交或回滚决议,而 TC 负责执行协调,这是一种合理且高效的设计,原因包括:
- TC 不理解业务逻辑:TC 无法判断事务状态是否满足业务需求,这需要 TM 的全局业务视角。
- 职责分离原则:让 TM 专注于业务逻辑,让 TC 专注于事务协调,降低系统复杂性。
- 避免错误判断:TC 无法处理失败状态中的特殊情况,如重试或补偿,而 TM 能基于业务语义做出正确决策。
- 系统灵活性和可扩展性:将决策逻辑放在 TM 中,而非 TC,能够更好地适应复杂多变的业务场景。
通过这种职责划分,分布式事务系统能够在保持业务灵活性的同时,确保全局事务的一致性与可靠性。这种设计在实际应用中,如 Seata 等分布式事务框架,已经被广泛采纳和验证。