白话分布式协议——2PC和3PC概述

213 阅读5分钟

分布式协议——2PC和3PC概述

所谓协议,就是交流的约定的流程和格式。

比如,当你在宿舍门口敲门,门内说:“天王盖地虎”,你答复:“宝塔镇河妖”,于是两个人就能否开门进去,达成了一致。

分布式一致性协议,自然比上面的复杂一些。

又比如,用户购买商品下单,会需要:

  • A活动服务:冻结优惠名额等资源
  • B支付服务:冻结付款金额(假设是平台代币)
  • C库存服务:冻结库存

A、B、C服务就下单动作,达成的资源状态一致:

  • 服务A扣掉优惠名额
  • 服务B扣掉付款金额,保存正确的余额(平台代币)
  • 服务C扣掉相应库存量

这一些列动作,是一个事务,要么全部达成,下单成功;要么全部操作不生效,下单失败。 但是分布式环境下,存在网络分区隔离,网络延时,服务宕机, 三态等问题,要实现这样的分布式事务,需要一种协议来让大家协作。

那以此为例,我们来理解一下2PC和3PC协议。

2PC

协议

定义:顾名思义,2 Phase Commit, 先发起人先拉票,大家都同意后,发起人再通知事务提交。

阶段一

image.png

  1. 发起preCommit: 通知服务A,B,C执行业务逻辑
  2. ack: ABC会分别检查前置条件,然后执行业务
    • 条件不满足,返回失败 ack fail
    • 条件满足,执行业务逻辑,返回成功 ack success, 锁定资源,等待下一步commit

阶段二

image.png

如果阶段一,大家都返回ack success, 那么就发起commit

  1. 发起commit请求
  2. ack:ABC执行本地事务提交,释放资源,并ack

如果阶段一,有人返回ack fail, 那么就发起rollback

  • 发起rollback请求
  • ack:ABC执行本地事务回滚,释放资源,并ack

异常

作为服务ABC,可能会面对的异常情况(按需脑补图例):

  1. 阶段一preCommit没有收到,无感
  2. 阶段二commit没有收到,资源处于锁定状态,如何应对:
    • 死等,不见不散,资源一直锁定 【不合适】
    • 等一段时间,如果commit一直未到达, 执行commit 【不合适:可能有的返回了ack fail,未执行业务逻辑】
    • 等一段时间,如果commit一直未到达, 执行rollback【恰当】

作为调用发起者Actor,可能会面对的异常情况(按需脑补图例):

  1. 阶段一ack没有全部收到:无法确定下游是否已经执行业务逻辑,
    • 可多次重试preCommit,然后发起回滚
  2. 阶段二ack没有全部收到:无法确定下游是否已经提交事务,
    • 多次重试commit
    • 部分已经commit,无法执行回滚

注意:服务ABC对每个操作都要有幂等性,便于调用者Actor重试。

总结

2PC实现细节:

  1. Actor在未收到ack时,可多次重试
  2. 服务ABC对操作要具有幂等性
  3. 阶段二中,当服务ABC等待commit超时,执行rollback

2PC存在不一致的情况:

  • 情况:第二步的commit,如果服务ABC有的没收到,那么只有部分执行commit,而另一部分等待超时后回滚,出现严重的数据不一致。

2PC的性能问题:

第一步中,并发情况可能会存在,服务ABC在一段时间内,总有不满足的情况,导致资源频繁加锁和释放锁

譬如有3个Actor,一起开始下单,可能存在多次加锁和释放锁 - Actor1发起preCommit,C不满足,AB满足(锁定资源), 随后Actor1发起AB资源释放 - Actor2发起preCommit,B不满足,AC满足(锁定资源), 随后Actor2发起AC资源释放 - Actor3发起preCommit,都满足,随后Actor3发起commit

如果是并发情况,因为部分人下大单,库存明显不足,部分人余额不足(假设是平台内代币),导致频繁且无意义的资源加锁和释放。

3PC

协议

定义:顾名思义,3 Phase Commit。 相对2PC,多了一个阶段。其实是将2PC的阶段一拆分为2个子阶段。

  • 阶段一(2PC.1-1): Actor发起preCheck,检查资源是否满足,不锁定资源(如库存够不够,余额够不够等)
  • 阶段二(2PC.1-2): Actor发起preCommit,服务ABC执行业务逻辑,锁定资源(同2PC的阶段一)
  • 阶段三 (2PC.2): Actor发起commit/rollback指令,服务ABC执行commit/rollback

3PC相对2PC的改进点分析

  • 增加前置检查阶段:解决2PC中的性能问题,减少无效的资源加锁和释放锁动作,或者说锁的粒度比2PC要小
  • 服务ABC在阶段二(2PC.1-2)之后,一直等待commit或rollback指令,等待超时后,执行commit(区别于2PC)
    • 既然可以进入阶段二(2PC.1-2),那么前一阶段已经检测通过,大家都表示资源没有问题,乐观认为基本可以commit

异常

3PC存在的不一致情况:并发情况下,多个Actor同时下单,可能会导致不一致

  • 阶段一(2PC.1-1): 多个Actor发起preCheck, 这时,服务ABC均返回条件满足
  • 阶段二(2PC.1-2):Actor1发起preCommit,随后,Actor1收到来自服务C的ack fail,库存不足了
  • 阶段三 (2PC.2):Actor1发起rollback,但是网络原因,A或B等待阶段三指令超时,执行commit, 出现不一致(钱和优惠券都用了,库存没减)

总结

所以3PC只是优化了2PC阶段一的性能,两者均无法保证达到一致性。 关键服务ABC可能等不到最后一个阶段的commit,此时无论选择rollback或commit,都有不一致的风险。

伏笔

到这里,我不由得想,Google Chubby的作者Mike Burrows说,世上只有一种一致性算法,那就是Paxos算法。那2PC和3PC既然无法保证达到一致性,这玩意存在意义是什么,TM会有人用么?

待续~