简单来说,分布式事务 指的是一个事务(Transaction)的操作单元分布在多个相互独立的服务(或系统)上,这些服务可能运行在不同的服务器上,甚至可能使用不同的数据库。这个事务要求所有参与的服务要么都成功执行(提交),要么都失败回滚(中止),从而保证整个业务流程的原子性。
在传统的单体应用中,事务通常只涉及一个数据库,可以使用数据库本身提供的事务机制(如 ACID 属性)来保证数据一致性。但在微服务架构中,一个业务操作往往需要跨越多个服务,每个服务管理自己的数据库,这就带来了分布式事务的挑战。
为什么需要分布式事务?
在微服务中,业务流程常常不是由单个服务完成的,而是由多个服务协同完成的。例如:
- 在线下单流程: 用户下单时,可能需要同时扣减商品库存(库存服务)、冻结用户余额或积分(订单服务/支付服务)、记录物流信息(物流服务)等。这些操作涉及不同的服务,如果其中任何一个操作失败,已经成功的操作就需要回滚,否则就会导致数据不一致(比如库存扣了但订单没生成,或者订单生成了但库存没扣)。
- 跨银行转账: 从 A 银行账户转账到 B 银行账户,需要 A 银行服务扣款和B 银行服务收款,这两个操作必须同时成功或同时失败。
分布式事务的挑战:
- 网络问题: 服务之间通过网络通信,网络延迟、中断或故障是常态,可能导致部分操作成功而部分失败。
- 数据一致性: 如何确保所有参与的服务最终达到一致的状态(全部成功或全部失败)是一个核心难题。
- 性能开销: 分布式事务通常比本地事务更复杂,需要协调多个服务,因此性能开销更大。
- 系统复杂性: 实现和管理分布式事务会增加系统的复杂度。
常见的分布式事务解决方案:
由于两阶段提交(2PC)等强一致性方案在微服务环境下通常被认为过于复杂且性能较差,实践中更常用的是一些最终一致性(Eventual Consistency)的方案:
-
Saga 模式:
- 基本思想: 将一个大的分布式事务拆分成一系列本地事务。前一个本地事务成功后,会触发下一个本地事务;如果某个本地事务失败,则需要执行一系列**补偿事务(Compensating Transaction)**来撤销之前已成功执行的事务的结果。
- 优点: 避免了 2PC 的阻塞问题,相对简单。
- 缺点: 实现补偿逻辑可能比较复杂,且在补偿失败时可能需要人工介入。保证的是最终一致性,在补偿完成前可能存在短暂的不一致状态。
- 实现方式:
- 编排式(Orchestration): 有一个中央协调者(Saga 协调器)来决定执行哪个本地事务,并在失败时执行补偿。
- 协同式(Choreography): 每个服务在本地事务完成后发布事件,其他服务监听这些事件并决定是否执行自己的本地事务。失败时也通过事件触发补偿。
-
TCC(Try-Confirm-Cancel)模式:
- 基本思想: 每个操作都分为三个阶段:
- Try: 预留资源,做业务检查。
- Confirm: 确认执行业务,使用 Try 阶段预留的资源。
- Cancel: 取消执行业务,释放 Try 阶段预留的资源。
- 协调过程: 协调者会先调用所有参与者的 Try 接口。如果都成功,则调用 Confirm;如果任何一步失败,则调用 Cancel。
- 优点: 相对能保证更强的一致性(比 Saga),适用于对一致性要求较高的场景。
- 缺点: 需要为每个操作额外实现 Try、Confirm、Cancel 三个接口,实现成本较高。
- 基本思想: 每个操作都分为三个阶段:
-
消息队列(Message Queue)最终一致性方案:
- 基本思想: 通过消息队列来解耦服务,并利用消息的可靠传递机制来保证最终一致性。一个服务在完成本地事务后,发送一个消息到 MQ;另一个服务消费这个消息并执行自己的本地事务。
- 关键点: 通常会结合"本地消息表"或"事务消息"等机制来保证"本地事务 + 消息发送"的原子性,例如:
- 服务 A 在执行本地事务时,同时将一条消息记录到自己的"本地消息表"中。
- 事务成功后,尝试将"本地消息表"中的消息发送到 MQ。
- 如果发送成功,则从"本地消息表"中删除该记录。
- 如果发送失败,则通过定时任务重试发送。
- 优点: 实现相对简单,利用 MQ 的可靠性机制。
- 缺点: 保证的是最终一致性,存在短暂的不一致窗口。
总结:
微服务中的分布式事务是为了解决跨多个独立服务的数据一致性问题。由于强一致性方案(如 2PC)的局限性,实践中更倾向于采用 Saga、TCC 或基于消息队列的最终一致性方案。选择哪种方案取决于业务对一致性的要求、系统的复杂度承受能力以及开发成本等因素。理解这些方案的核心思想及其优缺点,对于设计可靠的微服务系统至关重要。