2PC和3PC中的一些细节问题

简单总结下2PC3PC的一些细节问题。

2PC

2PC为两段提交,主要是通过先准备,再提交来实现分布式事务。2PC运行过程如下:

准备阶段:

事务协调者给没给参与者发送Prepare消息,每个参与者要么直接返回失败状态,要么在本地预先执行事务,比如:写redoundo log,但是不提交,然后发送第一阶段执行成功信息。

详细:

  1. 注意,准备阶段是2PC过程中,参与者唯一一次拒绝执行事务的机会,当参与者返回给协调者”同意“之后,意味着在之后的阶段,则需要完全听命与协调者,当协调者第二阶段发送提交时,参与者不可以再拒绝提交,不管出现任何异常情况,参与者都需要不断重试,知道成功提交事务。为什么第一阶段存在的意义,询问你是否能执行任务,并返回一个可靠的结果。
  2. 在参与者返回“同意”信息之后,意味着本地环境已经准备好,此时可能会事务将会一直阻塞,知道收到协调者的第二阶段的信息。

提交阶段:

当协调者收到参与者第一阶段失败的消息或超时的消息时,会给每个参与者发送回滚的消息;

当协调者收到所有参与者同意的消息时,则会发起提交消息;

参与者则需要根据协调者发送的消息进行相应的操作。

详细:

  1. 协调者的高可用:在协调者在发起请求之前,需要先将信息持久化到磁盘中,这样当协调者挂掉重启后,能够通过磁盘的信息检查事务的提交进度,进而修复上一个事务。
  2. 当协调者第二阶段做出决策后,不管是回滚还是还是提交,都需要确保所有的参与者成功收到信息,如果出现失败或超时,则需要一直重试,直到成功为止。

可以看到,2PC的关键因素就在于“信守承诺”:

  • 第一阶段,当参与者同意了请求,则需要保证哪怕后面任何时候出现问题,也要不断重试直到成功。
  • 第二阶段,当协调者做出决定之后,这个决定则不可改变。

正是这两个阶段的不可改变,使得2PC能够实现原子性。

2PC优缺点分析

2PC通过两次信守承诺的请求,实现了分布式一致性。

如果参与者在2PC期间发生失败:

  • 第一阶段宕机:协调者会发现其中某个参与者超时,则第二阶段会发起回退请求。
  • 第二阶段宕机:协调者会一直重试直到此参与者返回成功。

如果协调者2PC期间宕机:

不管哪个阶段宕机,由于协调者每次在发起请求前会保存状态,因此他可以在恢复后发起询问。

2PC的优点就是简单,但是缺点也很明显:

参与者高度依赖协调者,因此当协调者发生故障的时候,参与者只能一直阻塞直到协调者恢复,因此故障风险大,并且会影响系统的效率。

3PC

3PC相对于2PC而言增加了一个缓冲阶段,这个缓冲阶段主要就是为了解决2PC对协调者强依赖的问题,3PC提交过程如下:

第一阶段:CanCommit

这个阶段只是简单地检查参与者本身的健康信息以及和协调者的通信情况,协调者发送消息询问所有参与者,是否能进行事务,参与者简单检查自身情况后,根据情况进行回复。

第二阶段:PreCommit

此阶段类似2PC的第一阶段,参与者收到消息后,预先执行事务,并将UndoRedo信息记录到日志中,但是并不提交,当完成操作后,参与者将给协调者发送Ack信息表示已准备提交

第三阶段: DoCommit

第三阶段和2PC的第二阶段类似,协调者确定所有参与者返回了Ack信息之后,便会发起提交请求,参与者收到信息后执行提交操作,并返回成功给协调者。

2PC VS 3PC

相对于2PC而言,3PC增加了一个缓冲阶段:CanCommit,通过缓冲阶段能够让所有参与者预先准备事务,一般来说,如果事务能够进行到第二阶段,则表示大部分情况下,事务应该都能正常运行,比如模拟一个场景:

2PC :

X联系小A: 小A国庆的时候有空吗?我们想搞一个同学聚会。

A: 有的

X联系小B: 小B国庆的时候有空吗?我们想搞一个同学聚会。

B: 不行额,国庆要加班。


此时,小A并不知道国庆的同学聚会是否能够正常进行,而是需要依赖于X再次联系小A,告诉他其他人的情况,因此小A经过第一阶段后,便不能轻举妄动,只能一直阻塞等X的信息,这便是参与者对协调者的依赖关系。


3PC:

X联系小A: 小A国庆的时候有空吗?我们想搞一个同学聚会。

A: 有的

X联系小B: 小B国庆的时候有空吗?我们想搞一个同学聚会。

B: 有的


X继续联系小A: 小A啊,我刚刚联系了其他人,他们都同意了,时间定在10月1号,和平饭店,如果10月1号我没有给你打电话,你就直接去就行了哈。

A: 好的

X继续联系小B: 小B啊,我刚刚联系了其他人,他们都同意了,时间定在10月1号,和平饭店,如果10月1号我没有给你打电话,你就直接去就行了哈。

B: 好的


可以看到,3PC通过增加一个阶段,便减弱了参与者对协调者的依赖,比如在第一个阶段之后小A没有收到X的信息,那么小A便可以不去,因为他并不知道后面的人情况,而在第二个阶段,相当于做了一个约定,当小A收到此消息后,便默认第三阶段是commit,如果此后X宕机,那么小A在等待超时后,除非收到第三阶段回退的消息,否则小A依然会去参加同学聚会,因为一般在第一阶段答应过X之后,其他参与者都会尽力的去提交此次事务。

虽然3PC减弱了对协调者的依赖,但是带来的问题便是极端情况下数据可能不一致,因为3PC第三阶段如果超时,其他参与者便会默认提交,那么如果在某些特殊情况下,使得X发送的是abort消息,那么就会出现数据不一致的问题。