分布式两阶段提交(Two-Phase Commit,2PC)是分布式系统中常用的一种原子提交协议,用于保证在分布式事务中所有参与者要么全部提交事务,要么全部回滚事务,以维护数据的一致性。以下是关于它的详细介绍:
基本概念
为什么需要两阶段提交(2PC)
在分布式系统中,多个节点需要协同完成一项事务。如果有的节点成功,有的却失败了,那就会出现数据不一致的情况。举个银行转账的例子吧,从A账户转钱到B账户时,必须保证两个账户的余额同时更新。如果只更新了一个账户,那么就造成了数据不一致。(比如A账户转给B账户200元,A减200,B却没有改变,这明显账就对不上了,
两阶段提交就是为了解决这种分布式事务中的数据一致性问题而生的。
分布式两阶段提交将事务的提交过程分为两个阶段:准备阶段和提交阶段。在整个过程中,有一个协调者(Coordinator)和多个参与者(Participants)。协调者负责协调事务的提交过程,参与者负责执行事务操作并向协调者反馈执行结果。
两阶段提交算法包含2个角色:
- 协调者
协调者负责协调算法的各个阶段 - 参与者
参与到事务中执行事务操作
两个阶段的具体过程
- 第一阶段:准备阶段(Prepare Phase)
- 协调者向所有参与者发送
Prepare
消息,询问它们是否可以提交事务。 - 参与者收到
Prepare
消息后,会执行事务的所有操作,但不提交事务,而是记录事务的日志,并将执行结果(Yes
或No
)反馈给协调者。如果参与者执行事务操作失败,或者由于某种原因无法提交事务,就返回No
;如果一切正常,就返回Yes
。
- 协调者向所有参与者发送
- 第二阶段:提交阶段(Commit Phase)
- 情况一:所有参与者都返回
Yes
:协调者收到所有参与者返回的Yes
消息后,会向所有参与者发送Commit
消息,通知它们提交事务。参与者收到Commit
消息后,会正式提交事务,并释放事务执行过程中占用的资源。 - 情况二:有参与者返回
No
:如果协调者收到任何一个参与者返回的No
消息,或者在规定时间内没有收到所有参与者的反馈,就会向所有参与者发送Abort
消息,通知它们回滚事务。参与者收到Abort
消息后,会根据之前记录的日志回滚事务,撤销已经执行的操作,并释放资源。
- 情况一:所有参与者都返回
这里我们可以类比成一场重要的会议决策过程:
- 第一阶段 - 准备阶段:会议主持人(相当于协调者Coordinator)询问所有参会成员是否同意提案(相当于事务)。成员们检查自己这边的情况,然后回复是否可以接受该提案。
- 第二阶段 - 提交阶段:
- 如果所有人都同意,主持人宣布提案通过,所有人按照提案行动(相当于提交事务)。
- 如果有任何一个人不同意,主持人宣布提案取消,所有人撤销任何已经做出的动作(相当于回滚事务)。
这个过程中,协调者负责发起和指导整个流程,而参与者则是具体执行事务的节点。
2PC的第一阶段是投票环节,投票由协调者节点发起,可以进一步细分为以下步骤:
- 事务询问:协调者向所有的参与者发送事务预处理请求,称之为Prepare,并开始等待各参与者的响应。
- 执行本地事务:各个参与者节点执行本地事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向协调者报告说:“我这边可以处理了/我这边不能处理”。
- 各参与者向协调者反馈事务询问的响应:如果参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行,如果没有参与者成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。
第一阶段执行完后,会有两种可能。1、所有都返回Yes. 2、有一个或者多个返回No。
如果第一阶段所有的参与者都返回Yes,那么我们就可以继续执行2PC第二阶段的正常提交步骤:
- 协调者节点通知所有的参与者Commit事务请求;
- 参与者收到Commit请求之后,就会正式执行本地事务Commit操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
2PC第二阶段:异常回滚
如果任何一个参与者向协调者反馈了No响应,或者等待超时之后,协调者尚未收到所有参与者的反馈响应,那么我们就需要执行2PC第二阶段的回滚操作:
- 协调者节点通知所有的参与者Rollback请求;
- 参与者收到Rollback请求之后,就会正式执行本地事务Rollback操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
举例
假设有一个分布式电商系统,包含订单服务和库存服务。当用户下单时,需要在订单服务中创建订单记录,同时在库存服务中扣减相应的库存。这就涉及到一个分布式事务,需要使用两阶段提交来保证数据的一致性。
- 准备阶段:协调者向订单服务和库存服务发送
Prepare
消息。订单服务创建订单记录成功,返回Yes
;库存服务扣减库存成功,也返回Yes
。 - 提交阶段:协调者收到两个服务都返回
Yes
,于是向它们发送Commit
消息。订单服务和库存服务收到Commit
消息后,正式提交事务,完成整个下单流程。若库存服务扣减库存失败,返回No
,协调者就会发送Abort
消息给订单服务和库存服务,让它们回滚事务,撤销已经执行的操作,订单不会创建,库存也不会扣减。
优缺点
2. 性能问题
3. 数据不一致风险
4. 阻塞问题
5. 可扩展性问题
- 优点
- 数据一致性:能够有效保证在分布式系统中,多个参与者之间的事务操作要么全部成功提交,要么全部回滚,避免数据不一致的情况发生。
- 实现相对简单:相比于一些更复杂的分布式事务处理方案,两阶段提交的原理和实现相对较为简单,容易理解和应用。
-
1. 单点故障问题
- 协调者故障影响全局:在两阶段提交协议中,协调者处于核心地位,负责整个事务流程的调度和决策。如果协调者在事务执行过程中发生故障(如硬件故障、软件崩溃等),整个分布式事务将受到严重影响。
- 在准备阶段,如果协调者在发出
Prepare
消息后崩溃,参与者可能会一直等待协调者的后续指令,导致资源被长时间占用,无法释放。 - 在提交阶段,如果协调者在发送
Commit
或Abort
消息的过程中崩溃,部分参与者可能已经收到消息并执行了相应操作,而其他参与者没有收到消息,从而造成数据不一致。
- 在准备阶段,如果协调者在发出
- 缺乏协调者备份机制:虽然可以通过一些方式为协调者设计备份,但这会增加系统的复杂度和成本,并且在主协调者出现故障时,切换到备份协调者的过程也可能会引发新的问题。
- 多次消息交互开销大:两阶段提交需要协调者和参与者之间进行多次消息交互。在准备阶段,协调者要向所有参与者发送
Prepare
消息,然后等待所有参与者的响应;在提交阶段,协调者又要根据响应结果向所有参与者发送Commit
或Abort
消息。这些消息的发送和接收会消耗大量的网络带宽和时间,尤其是在参与者数量较多或者网络延迟较大的情况下,会显著降低系统的性能和吞吐量。 - 资源锁定时间长:在准备阶段,参与者会执行事务操作并锁定相应的资源,直到收到协调者的最终指令(
Commit
或Abort
)才会释放资源。由于整个两阶段提交过程需要多次消息交互,导致资源被锁定的时间较长,这会影响系统的并发性能,降低其他事务对这些资源的访问效率。 - 网络分区导致部分参与者状态不一致:在分布式系统中,网络分区是一种常见的问题。当发生网络分区时,协调者和部分参与者之间可能会失去通信联系。
- 如果在提交阶段发生网络分区,部分参与者可能收到了
Commit
消息并提交了事务,而另一部分参与者由于网络问题没有收到消息,仍然处于等待状态。当网络恢复后,就会出现数据不一致的情况。
- 如果在提交阶段发生网络分区,部分参与者可能收到了
- 参与者故障恢复后的不一致:如果某个参与者在事务执行过程中发生故障,恢复后可能无法准确恢复到正确的状态。例如,参与者在收到
Prepare
消息并返回Yes
后崩溃,恢复后可能不记得之前的操作状态,无法正确处理后续的Commit
或Abort
消息,从而导致数据不一致。
- 参与者等待协调者指令:在整个两阶段提交过程中,参与者在等待协调者的指令时会处于阻塞状态,无法进行其他事务操作。如果协调者出现问题或者网络延迟严重,参与者可能会被长时间阻塞,这不仅会影响系统的性能,还可能导致其他依赖这些参与者的事务也被阻塞,形成连锁反应,降低整个系统的可用性。
- 参与者数量受限:随着分布式系统中参与者数量的增加,两阶段提交的性能会急剧下降。因为协调者需要与每个参与者进行消息交互,参与者数量越多,消息的数量和处理复杂度就越高,系统的可扩展性受到限制。这使得两阶段提交在大规模分布式系统中的应用受到一定的局限。
分布式两阶段提交虽然在保证分布式事务一致性方面起到了重要作用,但也存在一些局限性,在实际应用中需要根据具体的业务场景和系统需求来权衡使用。