两阶段提交与三阶段提交
分布式事务
满足ACID(原子性、一致性、隔离性、持久性)的一组操作,可以被称为一个事务。随着计算机系统的发展,越来越多的采用分布式的架构来对外提供服务,但是,不同的机器的处理性能、存储性能、网络状态等各有不同,让分布式集群始终对外提供可用的一致性服务一直是需要处理的问题。
为了保证数据变更请求在整个分布式环境下正确地执行,不会导致部分服务器暂时崩溃导致整个集群提供的服务和数据不再相同,在整个分布式系统处理数据变更请求的过程中,需要引入分布式事务的概念。常见的提交方式有二阶段提交(Two-phase Commit,2PC)和三阶段提交(Three-phase commit,3PC)。
二阶段提交
二阶段提交的底层实现主要分成两个阶段:
- 请求阶段
- 提交阶段
请求阶段
协调者通知每个参与者准备提交;
参与者在本地执行事务:
- 执行成功后,并不提交,告知协调者自己本地已经执行成功;
- 执行失败后,告知协调者本地作业执行故障
提交阶段
协调者根据第一阶段收集到的参与者的返回信息,决定是否提交:
- 如果全部的参与者都反馈成功,那么协调者通知所有参与者提交事务
- 如果存在参与者反馈失败,则协调者通知所有参与者取消事务
收到全部参与者的成功commit信息后,完成本次提交
举例
还是使用经理和柜员的例子。银行有一个经理和三个柜员,客户举老爷子申请存钱1000元,需要最终三个柜员将举老爷子余额+1000记录下来。
需要注意这里经理向柜员发送proposal和提交是广播出去的。
故障分析
柜员侧出现故障或拒绝Proposal
假如一个柜员暂时离开了,或者柜员这边发现今天不能存入钱了,如下图所示:
第一阶段经理侧出现故障
假如经理突然寄了,那么时间可能正好在第一阶段或第二阶段。
第一阶段如果经理寄了,经理无法收到柜员的存钱处理完成的消息
则这个存钱事务一直没有被提交,举老爷子的钱也没有增加1000元。
第二阶段经理侧出现故障
如果在发送COMMIT的消息的过程中,发送了一部分之后,经理寄了
此时数据会产生不一致。若经理重启,首先看自己的日志到哪了,然后依次去询问柜员最新的commit信息。
二阶段提交存在的问题
- 参与者阻塞
若某个参与者一直没有完成,则需要等待他完成。例如某个柜员业务不熟悉操作很慢,那么客户和经理需要一直等待他完成操作,才能一起进入下一步 - 单点故障
若协调者寄了,那么参与者长期阻塞 - 存在数据不一致情况
若协调者在第二阶段挂了,那么会产生数据不一致的情况。
三阶段提交
三阶段提交在二阶段算法的基础上进行了优化和改进。如下图所示,在整个三阶段提交的过程中,相比二阶段提交,增加了预提交阶段。
canCommit阶段
协调者首先询问所有的参与者的状态,当前是否可以执行业务;如果可以\不可以执行,就直接返回可以/不可以。
preCommit阶段
协调者根据参与者canCommit阶段的响应来决定是否可以继续事务的preCommit操作。preCommit阶段和二阶段提交里面的请求阶段一致
协调者通知每个参与者准备提交;
参与者在本地执行事务:
- 执行成功后,并不提交,告知协调者自己本地已经执行成功;
- 执行失败后,告知协调者本地作业执行故障
doCommit阶段
协调者根据参与者preCommit阶段的响应来决定是否可以继续事务的doCommit操作。
发送doCommit后,若接收到了所有参与者的haveCommitted响应,则执行成功;若仅接收到了部分haveCommitted响应,则事务执行中断。
特点
相比2PC,引入超时机制,减少了阻塞问题。此外,增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为。
但是,仍然存在数据不一致问题,在第三阶段如果只有一个参与者收到了abort操作指令,协调者和其他的参与者都会超时,协调者和参与者都继续提交事务,默认为成功,导致数据不一致。
参考
两阶段提交和三阶段提交
二段提交协议与三段提交协议
28 彻底掌握二阶段提交三阶段提交算法原理
两阶段提交(Two-Phase Commit)