分布式事务

为什么分布式事务复杂?

在传统的单体应用中,事务由一个数据库管理,通过 ACID 特性(原子性、一致性、隔离性、持久性)来保证数据完整性。但在分布式系统中,这些保证被打破了:

  1. 独立性与自治性: 每个微服务拥有自己的数据库,且这些数据库可能是异构的(MySQL、PostgreSQL、MongoDB、Redis 等),它们没有全局的事务管理器。

  2. 网络不可靠性: 分布式系统依赖网络通信,网络延迟、分区或故障都可能导致通信失败,使得事务状态难以协调。

  3. 部分失败: 某个节点或服务失败可能导致整个事务停滞或数据不一致。

  4. CAP 定理: 在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)三者不可兼得。在实际应用中,通常需要在一致性和可用性之间做出权衡。


常见的分布式事务模式

为了解决分布式事务的复杂性,业界发展出了多种模式,它们在一致性、可用性和性能之间进行了不同的权衡。

1. 两阶段提交 (2PC - Two-Phase Commit)


2PC 是一种强一致性的分布式事务协议,它尝试在分布式环境中模拟单数据库事务的 ACID 特性。

角色:

  • 协调者 (Coordinator): 负责调度和协调所有参与者。

  • 参与者 (Participants): 执行事务分支操作的独立数据库或服务。

工作流程:

  1. 阶段一:提交请求(Prepare Phase)

    • 协调者向所有参与者发送 "事务准备" 请求。

    • 每个参与者执行本地事务操作,并将所有修改写入到预提交日志(Redo 和 Undo Log),但不真正提交。它会锁定所有必要资源。

    • 参与者回复协调者:“准备好 (Yes)”或“未准备好 (No)”。

  2. 阶段二:提交执行(Commit/Rollback Phase)

    • 如果所有参与者都回复 "准备好":

      • 协调者向所有参与者发送 "提交" 命令。

      • 参与者提交其本地事务,并释放所有资源。

      • 参与者回复协调者 "提交完成"。

    • 如果有任何一个参与者回复 "未准备好" 或协调者在第一阶段超时:

      • 协调者向所有参与者发送 "回滚" 命令。

      • 参与者回滚其本地事务,并释放所有资源。

      • 参与者回复协调者 "回滚完成"。

优点:

  • 保证了强一致性:要么所有操作都成功,要么都失败。

缺点:

  • 性能问题: 两个阶段的多次网络往返增加了延迟,在大并发场景下吞吐量低。参与者在准备阶段会锁定资源,直到整个事务完成,这会大大降低系统的可用性。

  • 单点故障: 协调者如果崩溃,可能导致所有参与者阻塞("活锁"),即它们持有资源但无法继续或回滚。

  • 数据不一致风险: 在极端情况下(例如第二阶段,协调者发出提交指令后,部分参与者接收并提交,但协调者或部分参与者在全部提交前崩溃),仍可能导致数据不一致。

适用场景:

  • 理论上适用于强一致性要求极高的场景,但由于其性能和可用性问题,在实际的互联网分布式系统中很少直接使用

2. 三阶段提交 (3PC - Three-Phase Commit)


3PC 是对 2PC 的改进,旨在解决 2PC 的阻塞问题。它在 2PC 的基础上引入了一个 "预提交" (PreCommit) 阶段,并在每个阶段都增加了超时机制

工作流程:

  1. 阶段一:CanCommit 阶段

    • 协调者询问参与者是否可以执行事务(类似于 2PC 的准备阶段,但参与者不锁定资源)。

    • 参与者回复。

  2. 阶段二:PreCommit 阶段

    • 如果所有参与者回复可以提交,协调者发送 "预提交" 请求。

    • 参与者执行事务操作,写入 Redo 和 Undo 日志,并锁定资源,但不提交

    • 参与者回复协调者 "预提交成功"。

  3. 阶段三:DoCommit 阶段

    • 如果所有参与者都回复预提交成功,协调者发送 "正式提交" 请求。参与者完成提交。

    • 如果有参与者在 PreCommit 阶段失败,或者协调者超时,则回滚。

优点:

  • 通过超时机制和预提交阶段,减少了 2PC 的阻塞时间,提高了可用性。

缺点:

  • 性能更差: 增加了更多的网络通信轮次。

  • 复杂性更高: 实现和维护难度更大。

  • 仍然存在数据不一致: 在某些特定极端情况(例如网络分区)下,3PC 仍然不能完全避免数据不一致。

适用场景:

  • 理论上比 2PC 更好,但由于其高复杂度和仍然存在的潜在不一致性,在实际应用中也很少直接使用

3. Saga 模式


Saga 模式是一种处理长活事务(Long-running Transactions)分布式事务的模式,它通过一系列的本地事务(Local Transactions)来维护数据一致性。每个本地事务都有一个对应的补偿事务(Compensation Transaction)。Saga 最终提供最终一致性

核心理念:

  • 一个分布式事务由一系列按顺序执行的本地事务(每个服务自己的数据库事务)组成。

  • 如果其中任何一个本地事务失败,Saga 会通过执行之前已成功本地事务的补偿事务来撤销(回滚)整个分布式事务。补偿事务不是回滚,而是语义上的撤销。

两种实现方式:

  1. 编排 (Orchestration) 模式:

    • 有一个中央的 Saga 协调器(通常是一个独立的服务)。

    • 协调器负责管理和协调每个本地事务的执行顺序和补偿逻辑。它通过直接调用服务 API 来告诉每个服务要执行哪个本地事务。

    • 如果某个服务执行失败,协调器会触发相应的补偿事务序列。

    • 优点: 逻辑集中管理,易于追踪事务流程。

    • 缺点: 协调器可能成为单点瓶颈或故障点;协调器代码相对复杂。

  2. 协作 (Choreography) 模式:

    • 没有中央协调器。每个服务在完成其本地事务后,会通过发布**领域事件(Domain Event)**来通知其他相关服务。

    • 其他服务订阅这些事件并执行自己的本地事务。

    • 如果出现问题(某个服务处理失败),通过发布补偿事件来启动回滚流程。

    • 优点: 服务之间高度解耦,扩展性好,没有单点瓶颈。

    • 缺点: 事务流程分散在各个服务中,难以追踪和理解整个分布式事务的完整路径和状态;补偿逻辑复杂,可能形成事件风暴。

优点:

  • 高可用性、高并发: 避免了 2PC/3PC 的强阻塞,系统吞吐量高。

  • 服务解耦: 各服务独立管理自己的数据和本地事务。

  • 易于扩展: 能够应对微服务架构的伸缩性需求。

缺点:

  • 最终一致性: 无法提供强一致性保证,事务在完成前可能处于中间不一致状态。

  • 复杂性: 补偿逻辑的实现和管理相对复杂,需要仔细设计,特别是应对幂等性和幂反性(补偿事务的幂等性)。

  • 测试困难: 调试和测试长活事务的整个流程(包括正向和补偿路径)更具挑战性。

适用场景:

  • 微服务架构中实现分布式事务的主流模式

  • 最终一致性有要求,但可以接受短时间内的不一致(因为补偿事务需要时间)。

  • 需要高可用性高性能的系统。

4. 事务消息 / 本地消息表模式


这是一种用于实现最终一致性的常用模式,特别适用于异步解耦高并发场景。它结合了数据库的本地事务消息队列的可靠性

工作原理:

  1. 发送方(本地事务):

    • 业务系统在本地数据库中执行核心业务操作,并同时在本地的**一张专门的消息表(或事件表)**中插入一条“待发送”的消息记录。

    • 这两步操作(核心业务操作和消息记录插入)被包装在同一个本地数据库事务中,保证原子性。

    • 关键: 只有当本地事务提交成功后,消息记录才真正持久化。

  2. 消息可靠发送:

    • 一个独立的消息发送服务/守护进程会定期扫描本地消息表,查找那些“待发送”的消息。

    • 它将这些消息发送到消息队列(Message Queue,如 Kafka, RabbitMQ, RocketMQ)

    • 消息发送成功后,更新本地消息表中的消息状态为“已发送”。

    • 如果发送失败,会进行重试。

  3. 接收方(异步处理):

    • 其他依赖此消息的服务(消费者)从消息队列中消费该消息。

    • 消费者执行其自身的本地业务逻辑和数据库事务。

    • 关键: 消费者必须保证其处理的幂等性,以防止因消息重复投递(消息队列可能重试)导致的问题。

优点:

  • 高可用性、高并发: 发送方和接收方完全解耦,异步执行,避免了分布式事务的阻塞。

  • 可靠性强: 结合了本地事务对消息的原子提交和消息队列的持久化机制,保证消息不丢失

  • 实现相对简单: 比 Saga 的补偿逻辑通常更直观,更易于理解和实现。

缺点:

  • 最终一致性: 数据在短时间内可能存在不一致状态(消息从发送到消费并处理有延迟)。

  • 依赖消息队列: 引入了消息队列的运维成本和可能的消息延迟。

  • 额外数据库操作: 需要在发送方额外维护一张本地消息表,并进行额外的数据库操作。

  • 消费者幂等性: 必须要求消费者实现幂等性,这是该模式的必备条件。

适用场景:

  • 异步通信服务解耦的场景。

  • 高并发下需要保证数据最终一致性。

  • 实时性要求不高,可以接受一定的延迟。

  • 例如,用户注册成功后,异步发送邮件、积分奖励、用户画像更新



你提得好!除了前面详细介绍的 2PC/3PC、Saga 和事务消息/本地消息表,分布式事务领域还有一些其他模式和概念,它们在特定场景下提供解决方案,或者是在现有模式基础上的扩展和变体。


5. TCC(Try-Confirm-Cancel)模式


TCC 是一种补偿事务模型,与 Saga 模式类似,但也存在显著差异。它强调的是业务层面的两阶段提交,而不是依赖底层数据源的事务。

核心理念:

  • 将一个完整的业务操作拆分为三个独立的、原子性的操作:

    • Try(尝试)阶段: 尝试执行业务。这个阶段通常是做预留和资源锁定,确保业务可以执行,但不是实际提交。例如,预扣库存、预冻结资金、检查资源是否可用。

    • Confirm(确认)阶段: 如果所有 Try 都成功,则执行 Confirm 操作,真正提交业务。例如,实际扣减库存、实际划转资金。

    • Cancel(取消)阶段: 如果任何一个 Try 失败,或者 Confirm 阶段失败,则执行 Cancel 操作,回滚 Try 阶段所做的预留或冻结。例如,释放预扣的库存、解冻资金。

优点:

  • 强一致性(业务层面): TCC 在业务层面尝试实现类似 2PC 的强一致性,通过 Try 阶段的预留和 Confirm/Cancel 的最终确认,确保数据状态的最终一致性。

  • 高并发、高性能: Try 阶段通常可以很快完成,并且 Confirm/Cancel 阶段的业务逻辑相对简单,避免了 2PC 的资源长时间锁定。

  • 支持异构系统: 可以在不同数据库、不同技术栈的服务之间实现事务。

缺点:

  • 业务侵入性强: 需要侵入业务代码,将业务逻辑拆分成 Try、Confirm、Cancel 三个阶段,增加了开发成本和复杂性。

  • 实现难度大: 需要保证 Confirm 和 Cancel 操作的幂等性原子性,以应对网络超时或重试导致的问题。

  • 数据一致性风险: 在 Confirm/Cancel 阶段失败的情况下,需要有完善的重试和人工干预机制来保证最终一致性。

适用场景:

  • 对数据一致性要求较高,接近实时一致性,且对性能有较高要求。

  • 需要跨多个服务/系统进行精确的资源预留和操作的场景,如复杂的金融交易、预订系统、库存管理。


6. 最大努力通知模式(Best-Effort Delivery)


最大努力通知模式是一种最终一致性的实现方式,它不保证事务的原子性,而是侧重于高可用性和最终的数据一致性。它通常是异步消息驱动的,并且不涉及补偿事务

核心理念:

  • 业务操作与消息发送分离: 业务方在完成自己的核心业务操作后(在本地事务中提交),会尽最大努力通知相关方(通常通过消息队列发送事件)。

  • 不可靠的消息投递与重试: 消息发送方不保证消息一定会被接收方成功处理,但会进行多次重试(最大努力)。

  • 接收方被动处理: 接收方监听消息,处理业务。如果处理失败,则可以由接收方自身进行重试或记录异常日志,待人工介入。发送方不关心接收方是否处理成功。

  • 对账机制: 为了弥补可能的消息丢失或处理失败,通常需要引入对账机制,定期核对数据一致性。

优点:

  • 高吞吐量、高并发: 完全异步,服务间高度解耦,性能极高。

  • 简单易实现: 相较于 Saga 和 TCC,代码侵入性低,逻辑更简单。

  • 高可用性: 单个服务失败不影响整个流程。

缺点:

  • 最弱的一致性保证: 仅保证最终一致性,且依赖于消息重试和人工对账,可能存在较长的不一致窗口。

  • 缺乏回滚能力: 没有明确的补偿机制,一旦数据发送出去,就很难回滚。

  • 不适用于强事务性操作: 不适合需要严格保证原子性或强一致性的业务(如扣款、库存)。

适用场景:

  • 对实时一致性要求不高,允许一定时间内的不一致。

  • 需要异步处理、服务解耦,且消息丢失或处理失败影响不大的通知场景。

  • 例如: 用户注册后异步发送欢迎邮件、新订单通知其他系统(如物流系统,即使通知失败也可以人工处理)。

  • 日志同步、数据同步等非核心业务。


7. 基于可靠事件队列的最终一致性(Eventual Consistency via Reliable Event Queue)


这个模式与“消息队列 + 本地消息表”模式高度重叠,可以说本地消息表是实现可靠事件队列的一种具体技术方案。但更广义的“可靠事件队列”也可能包含其他技术,比如基于数据库 Change Data Capture (CDC) 的方案。

核心理念:

  • 确保业务操作和事件/消息的发送是原子性的

  • 事件/消息一旦发出,必须保证被消费者至少消费一次(at-least-once delivery)。

  • 消费者需要具备幂等性

实现方式(除了本地消息表):

  • CDC (Change Data Capture): 数据库的 Binlog 或 Transaction Log 作为事件源。例如,Debezium 等工具可以捕获数据库的变更事件,并将其发布到消息队列。这种方式对业务代码零侵入,但增加了 CDC 工具的部署和维护。

优点:

  • 强可靠性: 保证事件不丢失,最终一致性高。

  • 解耦: 服务通过事件异步通信,高度解耦。

  • 可扩展性: 易于水平扩展。

缺点:

  • 最终一致性: 仍然是最终一致性。

  • 引入额外组件: 需要消息队列、(可能是)本地消息表或 CDC 工具的运维。

  • 消费者幂等性: 必须保证。

适用场景:

  • 微服务间异步通信的核心模式。

  • 需要严格保证事件不丢失且能够容忍最终一致性的场景。

  • 数据同步、状态变更通知、构建读写分离架构等。


总结选择考量

在选择分布式事务模式时,没有银弹,通常需要综合考量:

  • 一致性要求: 是需要强一致性(如金融交易的核心流程)还是可以接受最终一致性?

  • 业务复杂性: 业务流程是否适合拆分为 Try/Confirm/Cancel?是否能接受补偿逻辑?

  • 性能和吞吐量: 系统是否需要处理极高的并发?

  • 技术栈和基础设施: 是否有成熟的消息队列、CDC 工具,以及团队对这些技术的掌握程度?

  • 容错和可恢复性: 出现故障时,系统如何恢复,是否需要人工干预?

在实际的分布式系统中,往往会根据业务场景的特点,混合使用多种模式来满足不同的需求。例如,核心交易路径可能使用 TCC 或 Saga,而辅助通知类功能则使用事务消息或最大努力通知。

如何选择分布式事务模式?

选择哪种分布式事务模式,需要根据具体的业务需求、系统架构和对一致性、可用性、性能的权衡来决定:

  • 如果业务对强一致性有极其苛刻的要求(如金融核心交易系统),且对性能和可用性要求相对较低: 可能会考虑 2PC/3PC,但更常见的是采用单体架构或通过最终一致性 + 人工对账/补偿来规避分布式事务的复杂性。

  • 如果构建微服务架构,且能接受最终一致性:

    • Saga 模式:适用于涉及多个步骤、需要复杂补偿逻辑的长活事务。当业务流程本身就是多步骤的、可中断和可回滚的,Saga 很适用。

    • 事务消息/本地消息表模式: 适用于将一个核心操作作为事件,通知其他服务进行异步响应的场景。它强调消息的可靠投递和消费者幂等性。这是目前最常用且可靠的模式之一。

在实际项目中,往往会根据不同的业务场景,混合使用上述模式。理解它们的优缺点和适用场景是设计健壮分布式系统的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值