目录
分布式系统中往往是由分布在不同位置的多台机器组成,如何保证这些机器的数据一致性,成为分布式系统需要解决的问题之一。为了解决这一问题,出现了一大批经典的一致性协议和算法,其中最著名的是二阶段提交(2PC)、三阶段提交(3PC)和Paxos算法。
2PC
二阶段提交,在多台机器中选出一个“协调者”的组件来调度所有其他分布式节点(“参与者”)
执行流程:
阶段一:投票流程
- 协调者向所有参与者发送事务内容,询问是否可以执行事务操作,并等待响应
- 各参与者执行事务,并将undo和redo记录在日志中
- 各参与者返回响应,事务执行成功返回yes,失败返回no
阶段二:执行阶段
存在两种可能:
事务提交:如果所有参与者都返回yes,执行事务提交
- 协调者向所有参与者发布commit请求
- 参与者收到后,正式执行事务提交操作
- 参与者完成提交之后,向协调者返回ack响应
- 协调组收到所有参与者的ack之后完成事务提交
事务中断:任何一个参与者返回no响应或者超时
- 协调者向所有参与者发出回滚请求
- 参与者收到后,利用undo日志回滚
- 参与者完成回滚之后,向协调者返回ack响应
- 协调组收到所有参与者的ack之后完成事务中断
优点
原理简单、实现方便
缺点
同步阻塞:各参与者在等待其他参与者响应时,处于阻塞状态
单点问题:过度依赖协调者,协调者挂掉,整个流程挂掉
数据不一致(脑裂):协调者给一部分参与者发送提交请求之后挂掉,导致只有部分参与者提交了事务
太过保守:没有较为完善的容错机制,任何一个节点失败都会出现问题
3PC
改进了2PC,将2PC拆分,形成了CanCommit、PreCommit和doCommit 三个阶段
阶段一:CanCommit
- 协调者向所有参与者发送包含事务内容的CanCommit请求,询问是否可以执行事务操作
- 参与者收到后,检查自身状态,可以执行返回yes,否则返回no
阶段二:PreCommit(存在两种可能)
执行事务预提交:收到所有yes响应
- 协调者向所有参与者发送PreCommit请求,进入Prepared阶段
- 参与者收到后,执行事务操作,记录Undo和redo日志
- 如果参与者成功执行,则返回ack,同时等待最终指令:提交或终止
中断事务:任何一个参与者返回no或者超时
- 协调者向所有参与者发送中断请求
- 参与者中断事务
阶段三:doCommit(存在两种可能)
执行提交:收到所有参与者的ack响应
- 协调者向所有参与者发送doCommit请求
- 参与者收到后,执行事务提交操作
- 完成事务提交之后,返回ack
- 协调者收到所有ack,完成事务
中断事务:任意一个参与者返回no或者超时
- 协调者向所有参与者发送中断请求
- 参与者收到后,利用undo日志回滚
- 参与者完成回滚之后,发送ack
- 收到所有ack之后,中断事务
注意:一旦进入阶段三,可能存在以下故障:
协调者出现问题
协调者和参与者之间网络故障
这都会导致参与者无法收到协调者的doCommit或者abort请求,这时参与者会在等待超时之后继续进行事务提交
优点
相较于2PC,降低了参与者的阻塞范围,并且能够在出现单点故障后继续达成一致
缺点
三阶段提交协议在去除阻塞的同时也引人了新的问题,那就是在参与者接收到preCommit消息后,如果网络出现分区,此时协调者所在的节点和参与者无法进行正常的网络通信,在这种情况下,该参与者依然会进行事务的提交,这必然出现数据的不一致性。