二阶段提交(2pc)协议
1、 简介
二阶段提交算法是一个分布式一致性算法,强一致、中心化的原子提交协议,主要用来解决分布式事务问题。在单体spring应用中我们往往通过一个@Transactional注解就可以保证方法的事务性,但是在分布式场景下,serviceA往往会调用serviceB和ServiceC等有自己单独数据库的服务。如果serviceb成功了,servicec失败了,这种情况的回滚就需要用到分布式事务了。
2阶段提交包含两个阶段,即准备阶段和提交阶段。包含俩种角色,即协调者(Coordinator)和参与者(Participant)。协调者它就是协调整个事务的提交和回滚这两个操作,而参与者呢就是这个分布式事务中的事务资源拥有者。那么事务资源怎么去理解呢?图中的ServiceA可以类比为协调者,ServiceB和ServiceC类比为参与者。当SeriveB和ServiceC都执行成功后,协调者ServiceA才会提交本次的分布式事务。
2、 2pc第一阶段
准备阶段,是用于协调者通过Prepare消息,通知参与者去锁定一些事务资源。在下图这个案例中,第一步客户端它发起一个分布式事务给协调者,协调者就会广播Prepare请求给所有的参与者。所有的参与者收到这个操作之后呢它就会在本地记录一个事务日志,undo和redo日志。在这个过程中的所有的参与者也会锁定本次分布式事务,需要用到的一些事务资源。它会根据这个需要锁定的事务资源它会尝试去执行,如果它能够执行的话它就给协调者反馈一个yes的响应。如果不能够执行的话,它就反馈一个no响应。因为我们的参与者会给协调者一个yes或者no响应,二阶段提交也是分为两种情况,一种是提交,一种是回滚。当我们的协调者收到所有的参与者的响应都是yes的时候,它就会执行一个提交操作。然后进入到第二阶段。
3、 2pc第二阶段
第二阶段的协调者会向所有的参与者广播这个global commit消息,这个消息就是通知所有的参与者你可以提交本次的分支事务了。所有的参与者收到这个global commit消息之后,它就会根据第一阶段记录这个redo日志它就会提交本地的分支事务。提交成功之后,它就会返回一个ack响应给协调者,协调者收到所有的参与者成功的响应之后,它就会给客户端返回成功,也就是说本次的分布式事务呢就提交成功了。如果协调者在第一阶段的时候收到有一个参与者返回为no的情况的话,那它就会进入到global rollback的阶段也就是说全局回滚的阶段,协调者会向所有的参与者发送一个global rollback消息,每个参与者收到这个global rollback消息之后,它就会根据第一个阶段记录的undo日志,回滚到执行本次全局事务之前的状态,回滚成功之后它会给协调者返回一个ack响应,协调者收到所有ack后告诉客户端本次全局事务执行失败了。
4、 存在的问题
4.1 同步阻塞问题
一个比较明显的问题呢就是同步阻塞。因为协调者,它需要等待所有的参与者都给它一个yes的响应或者是no响应的时候,它才会进入第二阶段,所以协调者这里有个同步阻塞的问题,协调者要等待所有的参与者响应。而第二个阻塞的问题在于在第二阶段的过程中,参与者必须要等待协调者的global commit消息 或者是global rollback消息之后它才会执行第二阶段,因为第一阶段prepare的时候,参与者锁定了本次分支事务的事务资源,锁定了事务资源后其他事务时不能访问的,如果协调者宕机了,没法通知参与者去执行第二阶段,那么参与者就释放不了事务资源。
4.2 2PC模型中可能出现的数据不一致问题
在2PC模型中,第一阶段是准备阶段。在这个阶段,协调者向参与者发送准备请求,要求参与者准备进行事务提交。
如果协调者在第一阶段崩溃,以下情况可能发生:
- 参与者等待超时:
参与者可能一直等待协调者的消息,如果协调者崩溃,参与者可能会无限期地等待下去。 - 参与者提交事务:
在第一阶段中,参与者接收到准备请求后,会将事务准备好以等待提交。如果协调者崩溃后,参与者可能会提交自己的事务,因为它无法得知协调者是否要求回滚事务。
由于以上情况,数据不一致的情况是可能发生的。如果协调者崩溃,部分参与者可能已经提交了事务,而其他参与者可能还在等待或者准备回滚事务。这种情况下,数据在不同参与者之间就会不一致。因此,在第一阶段中,协调者的崩溃可能导致数据不一致的情况发生。
4.3单点故障:
2PC算法中的协调者节点是一个关键节点。如果协调者节点发生故障,整个系统将无法继续进行任务的提交,因为参与者节点需要等待协调者的指令。这种单点故障可能导致系统的可用性降低。
4.4脑裂(Split-Brain)问题:
在某些情况下,如果网络分区或故障导致协调者和参与者之间的通信中断,系统可能会出现脑裂现象。即,一部分参与者可能收到了提交请求并执行了事务,而另一部分参与者则没有收到请求并保持原始状态,从而导致数据不一致。
4.5过于保守
任何一个参与者出现问题,都会影响整个事务的进程。
为了解决这些问题,人们提出了多种改进方案,如三阶段提交协议(3PC)和基于Paxos或Raft等算法的分布式一致性协议。这些改进方案在保持数据一致性的同时,也试图降低阻塞、单点故障和数据不一致的风险,并提高系统的性能和可用性。