首先了解一下为什么要使用多阶段提交协议这个东西。
在分布式系统中,我们会存在一个方法对多个库进行插入或更新。但是每个节点只能知晓自己的操作是成功或失败,却无法知道其他节点的成功和失败。这样的话就会导致数据的不一致性。当一个事务跨越了多个节点时,为了保持数据的一致性,所以就引进了两阶段提交协议这个东西。
所谓两阶段提交协议:第一阶段:准备阶段(投票阶段)
第二阶段:提交阶段(执行阶段)
在两阶段提交协议中,系统一般包含两类节点。一、协调者。二、参与者
两个阶段的执行步骤:
- 协调者向所有的参与者请求能不能处理事务,参与者会告知协调者能与不能。如果能就把协调告知的事务执行,但不提交。不能就直接返回。
- 协调者会获取到所有参与者返回的结果。当所有的参与者同意提交事务,协调者才会通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。
两阶段提交的缺点
- 同步阻塞的问题。
在执行过程中,所有参与者节点都是事务阻塞型的。当参与者占有公共资源时,其他的节点访问公共资源时不得不处于阻塞状态,导致系统处理效率下降。
2.单点故障。
当执行第二阶段时,协调者发生故障,参与者会一直阻塞下去。
3.数据不一致。在执行第二阶段时,当协调者想参与者发送提交的请求之后,发生了局部网络异常或者在发生commit请求过程中协调者发生故障,会导致只有一部分参与者接收到了commit请求。
两阶段提交无法解决的问题
当协调者出错,同时参与者也出错时,两阶段无法保证事务执行的完整性。考虑到协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者也同时宕机。那么即使协调者通过选举协议产生了新的协调者,这条事务额状态也不是确定的,没人知道事务是否被提交。
三阶段提交协议:
执行步骤
- CanCommit阶段
- 事务询问,协调者向参与者发送precommit请求。询问是否可以执行事务。
- 响应反馈参与者接到Cancommit请求。
- PreCommit阶段
- 协调者根据参与者的返回来决定是否继续事务的precommit操作。但是会出现两种情况
- 协调者从所有参与者获得反馈都是yes,那么事务会继续执行
- 如果有任何一个参与者向协调者发送了No响应,或者等待超时,协调者没有接收到参与者的响应那么事务中断。
- DoCommit阶段
- 执行提交
- 1.发送提交请求协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
- 2.事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
- 3.响应反馈 事务提交完之后,向协调者发送Ack响应。
- 4.完成事务 协调者接收到所有参与者的ack响应之后,完成事务。
- 事务中断,协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。
- 1.发送中断请求 协调者向所有参与者发送abort请求
- 2.事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
- 3.反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息
- 4.中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
- 执行提交
三阶段与二阶段的不同点在于。
- 对于协调者和参与者都设置了超时机制(2阶段中只有协调者有)。协调者如果在一定时间内没有收到参与者的消息则默认失败,参与者无法及时收到协调者的信息,他会默认执行commit,不会一直持有事务。
- 解决了单点故障,并减少了阻塞。但是也会带来数据不一致的问题。如果网络出错,协调者发送的请求没有及时被参与者收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。