分布式一致性协议
分布式事务
2PC
它可以保证在分布式事务中,要么所有参与进程都提交事务,要么都取消事务,即实现 ACID 的原子性(A)。
在数据一致性中,它的含义是:要么所有副本(备份数据)同时修改某个数值,要么都不更改,以此来保证数据的强一致性。
2PC分为2个阶段:
表决阶段:
1、事务询问
Coordinator (协调者)向所有的参与者发送一个 vote request
2、执行事务
各个参与者节点执行事务操作,并讲Undo和Redo信息记入事务日志中
3、各参与者向协调者反馈事务询问的响应.
如果参与者成功执行了事务操作,那么就反馈给协调者vote_commit响应,表示事务可以执行,如果没有参与者成功执行事务,那么就反馈给协调者vote_abort响应,表示事务不可以执行.
提交阶段:
Coordinator 收到所有参与者的表决信息,如果所有参与者一致认为可以提交事务,那么 Coordinator 就会发送 GLOBAL_COMMIT 消息,否则发送 GLOBAL_ABORT 消息;对于参与者而言,如果收到 GLOBAL_COMMIT 消息,就会提交本地事务,否则就会取消本地事务。
2PC一致性问题
2PC 在执行过程中可能发生 Coordinator 或者参与者突然宕机的情况,在不同时期宕机可能有不同的现象。
情况 | 分析及解决方案 |
Coordinator 挂了,参与者没挂 | 这种情况其实比较好解决,只要找一个 Coordinator 的替代者。当他成为新的 Coordinator 的时候,询问所有参与者的最后那条事务的执行情况,他就可以知道是应该做什么样的操作了。所以,这种情况不会导致数据不一致。 |
参与者挂了(无法恢复),Coordinator 没挂 | 如果挂了之后没有恢复,那么是不会导致数据一致性问题。 |
参与者挂了(后来恢复),Coordinator 没挂 | 恢复后参与者如果发现有未执行完的事务操作,直接取消,然后再询问 Coordinator 目前我应该怎么做,协调者就会比对自己的事务执行记录和该参与者的事务执行记录,告诉他应该怎么做来保持数据的一致性。 |
2PC优缺点
简单总结一下 2PC 的优缺点:
优点:原理简洁清晰、实现方便;
缺点:同步阻塞、单点问题、某些情况可能导致数据不一致。
关于这几个缺点,在实际应用中,都是对2PC 做了相应的改造:
同步阻塞:2PC 有几个过程(比如 Coordinator 等待所有参与者表决的过程中)都是同步阻塞的,所有参与该事务操作的逻辑都处于阻塞状态,各个参与者在等待其他参与者响应的过程中,将无法进行其他任何操作。在实际的应用中,这个问题是通过超时判断机制来解决的,但并不能完全解决同步阻塞问题;
Coordinator 单点问题:实际生产应用中,Coordinator 都会有相应的备选节点;
数据不一致:这个在前面已经讲述过了,如果在第二阶段,Coordinator 和参与者都出现挂掉的情况下,是有可能导致数据不一致的。
3PC
三阶段提交协议(Three-Phase Commit, 3PC)最关键要解决的就是 Coordinator 和参与者同时挂掉导致数据不一致的问题,所以 3PC 把在 2PC 中又添加一个阶段,这样三阶段提交就有:CanCommit、PreCommit 和 DoCommit 三个阶段。
- CanCommit
1.事务询问协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。
2.响应反馈参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No
- PreCommit
执行事务预提交:如果 Coordinator 接收到各参与者反馈都是Yes,那么执行事务预提交:
- 发送预提交请求:Coordinator 向各参与者发送 preCommit 请求,并进入 prepared 阶段;
- 事务预提交:参与者接收到 preCommit 请求后,会执行事务操作,并将 Undo 和 Redo 信息记录到事务日记中;
- 各参与者向 Coordinator 反馈事务执行的响应:如果各参与者都成功执行了事务操作,那么反馈给协调者 ACK 响应,同时等待最终指令,提交 commit 或者终止 abort,结束流程;
中断事务:如果任何一个参与者向 Coordinator 反馈了 No 响应,或者在等待超时后,Coordinator 无法接收到所有参与者的反馈,那么就会中断事务。
- 发送中断请求:Coordinator 向所有参与者发送 abort 请求;
- 中断事务:无论是收到来自 Coordinator 的 abort 请求,还是等待超时,参与者都中断事务
- doCommit
执行提交
- 发送提交请求:假设 Coordinator 正常工作,接收到了所有参与者的 ack 响应,那么它将从预提交阶段进入提交状态,并向所有参与者发送 doCommit 请求;
- 事务提交:参与者收到 doCommit 请求后,正式提交事务,并在完成事务提交后释放占用的资源;
- 反馈事务提交结果:参与者完成事务提交后,向 Coordinator 发送 ACK 信息;
- 完成事务:Coordinator 接收到所有参与者 ack 信息,完成事务。
在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 )
中断事务:假设 Coordinator 正常工作,并且有任一参与者反馈 No,或者在等待超时后无法接收所有参与者的反馈,都会中断事务
- 发送中断请求:Coordinator 向所有参与者节点发送 abort 请求;
- 事务回滚:参与者接收到 abort 请求后,利用 undo 日志执行事务回滚,并在完成事务回滚后释放占用的资源;
- 反馈事务回滚结果:参与者在完成事务回滚之后,向 Coordinator 发送 ack 信息;
- 中断事务:Coordinator 接收到所有参与者反馈的 ack 信息后,中断事务。
3PC 分析
3PC 虽然解决了 Coordinator 与参与者都异常情况下导致数据不一致的问题,3PC 依然带来其他问题:比如,网络分区问题,在 preCommit 消息发送后突然两个机房断开,这时候 Coordinator 所在机房会 abort, 另外剩余参与者的机房则会 commit。
而且由于3PC 的设计过于复杂,在解决2PC 问题的同时也引入了新的问题,所以在实际上应用不是很广泛。
2PC与3PC的区别
相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。