分布式事务
分布式事务是指在分布式系统中,涉及到多个节点(通常是多个数据库或者服务)的操作序列,这些操作要么全部成功,要么全部失败,以保持系统数据的一致性。分布式事务的挑战在于系统需要跨网络、跨多个独立操作单元来维护事务的原子性、一致性、隔离性和持久性(ACID属性)。
分布式事务实现方案
两阶段提交(2PC)
两阶段提交(2PC)是一种经典的分布式事务处理协议,用于在分布式系统中确保多个节点上的操作要么全部成功,要么全部失败,从而保持数据的一致性。
两阶段提交的参与者
- 协调者(Coordinator):负责协调事务的提交或回滚决策的节点。
- 参与者(Participants):执行事务的一部分,并响应协调者请求的节点。
阶段一:准备阶段(Prepare Phase)
- 事务执行:协调者首先指示所有参与者开始执行事务。参与者执行事务但不提交,只是记录操作日志,包含事务标识符(事务ID)和要执行的操作,以确保可以在后续阶段进行提交或回滚。
- 投票请求:一旦参与者完成了事务的执行,协调者向所有参与者发送一个准备(Prepare)请求,询问它们是否可以提交事务。
- 投票响应:每个参与者收到准备请求后,会做以下判断:
- 如果参与者能够提交事务,它会将事务的所有更改记录到持久存储中,并回复“准备好”( Prepared)给协调者。
- 如果参与者无法提交事务(例如,因为资源不足或发生错误),它会回复“拒绝”(Aborted)给协调者。
阶段二:提交阶段(Commit Phase)
根据准备阶段的投票结果,协调者将决定是提交还是回滚事务:
- 提交请求:如果所有参与者都回复“准备好”,协调者会向所有参与者发送提交(Commit)请求。
- 提交事务:参与者收到提交请求后,正式提交事务,释放所有占用的资源,并回复“提交确认”(Committed)给协调者。
- 回滚请求:如果有任何一个参与者回复“拒绝”,或者协调者在等待响应时超时,协调者会向所有参与者发送回滚(Rollback)请求。
- 回滚事务:参与者收到回滚请求后,撤销事务的所有更改,释放资源,并回复“回滚确认”(Aborted)给协调者。
两阶段提交的优缺点
优点
- 简单性:2PC协议相对简单,易于理解和实现。
- 强一致性:确保分布式事务的原子性,所有参与者要么全部提交,要么全部回滚。
缺点
- 单点故障:协调者是一个单点故障点,如果协调者发生故障,参与者可能无法决定事务的状态。
- 阻塞:在准备阶段,参与者需要锁定资源,直到事务最终提交或回滚,这可能导致资源长时间被锁定。
- 同步阻塞:2PC是同步操作,需要所有参与者都响应后才能继续,这可能导致性能问题。
- 无法处理协调者故障:在协调者故障的情况下,参与者可能无法确定下一步操作,导致事务悬而未决。
三阶段提交(3PC)
三阶段提交(3PC)是对两阶段提交(2PC)的改进,旨在解决2PC在协调者故障情况下的问题。3PC通过引入额外的预提交阶段来减少参与者的阻塞时间,并增加了一些额外的机制来处理协调者故障的情况,但它仍然无法完全避免所有问题,例如网络分区。
3PC将事务的提交过程分为三个阶段:准备阶段、预提交阶段和提交阶段。
阶段一:准备阶段(Prepare Phase)
- 事务执行:与2PC类似,协调者指示所有参与者开始执行事务。参与者执行事务但不提交,记录必要的信息。
- 投票请求:协调者向所有参与者发送准备请求,询问它们是否能够提交事务。
- 投票响应:参与者收到准备请求后,进行事务的可行性检查。如果可以提交,则回复“准备好”(CanCommit),否则回复“拒绝”(Abort)。
阶段二:预提交阶段(Pre-Commit Phase)
- 预提交请求:如果所有参与者都回复“准备好”,协调者进入预提交阶段,并向所有参与者发送预提交请求。
- 预提交响应:参与者收到预提交请求后,它们会承诺在阶段三提交事务,并记录这个承诺(在本地日志中记录事务的状态,以便在必要时进行回滚或提交)。然后,它们回复“预提交确认”(Pre-Committed)给协调者。
阶段三:提交阶段(Commit Phase)
这一阶段根据预提交阶段的结果来决定是否最终提交事务。
- 提交请求:如果协调者从所有参与者那里都收到了“预提交确认”,它将发送提交请求。
- 提交事务:参与者收到提交请求后,正式提交事务,释放所有资源,并回复“提交确认”(Committed)给协调者。
- 超时提交:如果协调者在预提交阶段收到所有参与者的“预提交确认”,但在发送提交请求前故障,参与者会等待协调者的提交请求。如果参与者等待超时,它们会认为协调者已经故障。根据预提交阶段的承诺,它们会自主提交事务。这是3PC与2PC的一个重要区别,3PC允许参与者自行决定提交,以避免长期阻塞。
- 回滚请求:如果在预提交阶段有参与者回复“拒绝”,或者协调者在准备阶段收到“拒绝”,协调者将发送回滚请求。
- 回滚事务:参与者收到回滚请求后,撤销事务的所有更改,释放资源,并回复“回滚确认”(Aborted)给协调者。
三阶段提交的优缺点
优点
- 减少阻塞时间:在预提交阶段,参与者已经做出提交的承诺,这可以减少资源锁定的时间。
- 处理协调者故障:与2PC相比,3PC可以在协调者故障时,让参与者自主决定提交事务,减少了协调者单点故障的影响。
缺点
- 复杂度增加:3PC引入了额外的阶段和超时机制,使得协议比2PC更加复杂。
- 网络分区问题:在网络分区的情况下,3PC可能会出现不一致的情况。例如,如果参与者无法与协调者通信,但其他参与者可以,那么参与者可能会在超时后自主提交,导致不同参与者之间的事务状态不一致。
3PC虽然比2PC有所改进,但它仍然不能保证在所有情况下都能保持一致性。在网络分区严重的情况下,3PC可能无法提供原子性保证。因此,在设计分布式系统时,通常需要结合其他技术,如幂等性操作、补偿事务和最终一致性模型,来确保系统的整体可靠性。
补偿事务(TCC)
TCC(Try-Confirm-Cancel)是一种柔性事务,将一个业务操作分解为三个步骤:尝试执行、确认执行和取消执行。TCC分为三个阶段:尝试(Try)、确认(Confirm)和取消(Cancel)。
- Try:执行业务操作,预留必要的资源,但不提交事务。
- Confirm:如果所有服务都成功执行了Try操作,则执行Confirm操作,正式提交事务。
- Cancel:如果有服务执行Try操作失败,则执行Cancel操作,回滚事务。
阶段一:尝试(Try)
在尝试阶段,各个服务会尝试执行事务中的操作,并且预留必要的资源。这些操作通常是幂等的,也就是说,即使多次执行,结果也是一样的。
- 预留资源:服务执行业务操作,但不真正提交它们。例如,在一个电商系统中,可能需要预留库存,但不实际扣减库存。
- 锁定资源:尝试阶段可能会锁定资源,以防止其他事务使用这些资源。
- 记录事务日志:服务记录足够的信息,以便在后续阶段能够回滚或确认操作。
阶段二:确认(Confirm)
如果尝试阶段所有服务的操作都成功,那么整个事务进入确认阶段。在这个阶段,各个服务会提交在尝试阶段预留的操作。
- 提交操作:服务提交在尝试阶段预留的业务操作。例如,实际扣减库存。
- 释放资源:确认操作完成后,释放所有锁定的资源。
- 清理事务日志:确认操作成功后,清理事务日志。
阶段三:取消(Cancel)
如果尝试阶段中有任何服务失败,或者整个事务失败,那么事务将进入取消阶段。在这个阶段,所有服务需要撤销它们在尝试阶段预留的操作。
- 撤销操作:服务撤销在尝试阶段预留的业务操作。例如,释放之前预留的库存。
- 释放资源:撤销操作后,释放所有锁定的资源。
- 清理事务日志:取消操作完成后,清理事务日志。
TCC的关键点
- 幂等性:尝试、确认和取消操作必须是幂等的,这样即使操作被重复执行,也不会影响最终结果。
- 资源预留:在尝试阶段,服务必须能够预留足够的资源,以确保事务能够顺利完成。
- 最终一致性:TCC保证的是最终一致性,而不是实时一致性。这意味着在事务执行过程中,系统的状态可能会短暂不一致,但最终会达到一致状态。
- 容错性:TCC允许部分服务失败,通过补偿事务来恢复一致性。
TCC的优缺点
优点
- 灵活性:TCC允许在分布式环境中实现复杂的事务逻辑。
- 容错性:通过补偿事务,TCC能够处理部分服务失败的情况。
- 性能:由于资源预留和操作提交是分开的,TCC可以减少资源锁定的时间,提高系统性能。
缺点
- 复杂性:实现TCC需要开发者编写额外的逻辑来处理尝试、确认和取消操作,这增加了系统的复杂性。
- 业务侵入性:TCC需要业务逻辑明确地支持预留资源和补偿操作,这可能需要对现有业务逻辑进行改造。
- 事务日志管理:正确管理事务日志是确保TCC正确性的关键,这要求系统有可靠的事务日志管理机制。
应用场景
TCC适用于以下场景:
- 跨服务事务:当事务涉及多个服务,并且这些服务需要协调一致地完成操作时。
- 长事务:事务可能持续很长时间,需要保证最终一致性。
- 柔性事务:系统可以容忍短暂的不一致性,但最终需要保证数据的一致性。
TCC适用于那些对事务响应时间敏感且需要强一致性的场景,尤其是在微服务架构中。例如,在电子商务平台上的订单处理、支付结算等关键业务流程中,TCC能够有效保障交易的完整性和准确性。此外,对于银行转账等金融领域应用,TCC也是一种可行的选择,因为它能够在一定程度上解决传统分布式事务带来的性能瓶颈问题。
Saga模式
Saga模式通过一系列的本地事务组成一个全局事务,每个本地事务都有一个对应的补偿事务。如果某个本地事务失败,则可以通过执行其对应的补偿事务来回滚之前已经成功执行的事务,从而保证整个全局事务的一致性。
Saga的基本概念
- 本地事务:每个服务内部执行的事务,这些事务可以独立提交,不需要等待其他服务的事务。
- 全局事务:由多个本地事务组成,这些本地事务共同完成一个业务目标。
- 补偿事务:每个本地事务都有一个对应的补偿事务,用于在本地事务失败时撤销其操作。
Saga的两种实现方式
同步Saga( Choreography )
在同步Saga中,没有中央协调器,每个服务负责决定何时执行本地事务以及何时发送消息给其他服务以触发它们的本地事务或补偿事务。
步骤:
- 服务A开始执行本地事务TA,并在成功后发送消息给服务B。
- 服务B接收到消息后,执行本地事务TB,并在成功后发送消息给服务C。
- 依此类推,直到所有服务完成它们的本地事务。
- 如果某个服务执行本地事务失败,它会发送消息给之前的服务,请求执行补偿事务。
异步Saga( Orchestration )
在异步Saga中,有一个中央协调器(通常是一个单独的服务或组件),它负责编排各个服务的本地事务和补偿事务。
步骤:
- 中央协调器发送指令给服务A,要求执行本地事务TA。
- 服务A执行本地事务TA,并在成功后通知中央协调器。
- 中央协调器接收到通知后,发送指令给服务B,要求执行本地事务TB。
- 依此类推,直到所有服务完成它们的本地事务。
- 如果某个服务执行本地事务失败,中央协调器会负责通知之前的服务执行它们的补偿事务。
Saga的优缺点
优点
- 灵活性:Saga模式允许在分布式环境中实现复杂的事务逻辑。
- 容错性:通过补偿事务,Saga能够处理部分服务失败的情况。
- 性能:由于每个本地事务可以独立提交,Saga可以减少资源锁定的时间,提高系统性能。
缺点
- 复杂性:实现Saga需要开发者编写额外的逻辑来处理补偿事务,这增加了系统的复杂性。
- 业务侵入性:Saga需要业务逻辑明确地支持补偿操作,这可能需要对现有业务逻辑进行改造。
- 事务管理:特别是在异步Saga中,中央协调器的实现和维护相对复杂。
应用场景
Saga适用于以下场景:
- 跨服务事务:当事务涉及多个服务,并且这些服务需要协调一致地完成操作时。
- 长事务:事务可能持续很长时间,需要保证最终一致性。
- 柔性事务:系统可以容忍短暂的不一致性,但最终需要保证数据的一致性。
其他方案
本地消息表(Local Message Table)
- 在本地数据库中维护一个消息表,用于记录事务的执行状态。
- 通过消息的发送和接收来保证事务的最终一致性。
消息队列(Message Queue)
- 使用消息队列来异步处理跨服务的事务操作。
- 通过消息的确认机制来保证事务的最终一致性。
事务消息(Transaction Message)
- 结合消息队列和数据库事务的特性,确保消息的发送和数据库操作的原子性。
- 消息队列支持事务消息的发送,只有当数据库事务提交后,消息才会被投递。
AT模式(Automatic Transaction)
- 基于Seata等分布式事务框架,自动在业务代码中加入数据操作的补偿逻辑。
- 通过框架自动处理分支事务的提交和回滚。
最大努力通知(Best Effort Notification)
- 服务在完成本地事务后,尝试通知其他服务,但不保证通知一定成功。
- 其他服务通过定时任务或补偿机制来处理可能的通知失败。
可靠事件模式(Reliable Event)
- 事件的发布和消费都是可靠性的,确保事件最终会被正确处理。
- 通常结合事件日志和补偿事务来实现。