深入理解分布式共识算法(一)——2pc_3pc

分布式事务问题

img

通常单节点事务比较简单,Spring 提供的 @Transaction 注解能够实现。但是在分布式场景下,比如 ServiceA 调用 ServiceBServiceC,每个服务分别操作各自的数据库,如果某个服务调用成功、另外一个调用失败,就会造成数据的不一致性,这就是分布式事务问题。

2PC

二阶段提交是一种 强一致性、中心化的原子提交协议

  • 强一致性:体现在所有的参与者都能实时保持一致的数据、一致的状态。
  • 中心化:分为协调者和参与者。
    • 协调者负责协调整个事务的提交和回滚操作,负责推进分布式事务;比如上图的 ServiceA;
    • 参与者是事务资源的拥有者,比如上图的 ServiceB、ServiceC;
  • 原子提交:分布式事务中所有协调者要么全部成功,要么全部失败。

准备阶段

img

  • 客户端发起一个分布式事务到协调者,协调者向所有的参与者广播 Prepare 请求;
  • 参与者在收到 Prepare 请求后会记录事务日志(RedoLogUndoLog),然后根据这个事务需要锁定的资源尝试去执行,根据是否能够执行事务反馈协调者 Yes/No 响应;
  • 协调者接收到所有参与者的响应,如果全部为 Yes,就会进入提交阶段。
  • 协调者如果接收到一个参与者的 No 响应,就会进入 global rollback 阶段,进行全局回滚。

提交阶段

img

  • 协调者通知所有参与者可以提交本次分支事务,发送 global commit 请求;
  • 参与者收到请求后,根据准备阶段记录的 RedoLog 日志,提交本地的分支事务;
  • 参与者在提交事务成功后,就会返回 ack 响应给协调者;
  • 协调者在接收到所有参与者成功的响应之后,返回给客户端成功。

全局回滚阶段

img

  • 协调者向所有参与者发送 global rollback 请求;
  • 参与者收到请求后,会根据本地的 undoLog 回滚本地事务,回滚成功后返回给协调者 ack 响应;
  • 协调者接收到所有 ack 响应之后,返回给客户端本次分布式事务执行失败。

隐藏问题

  • 数据不一致问题:二阶段回滚或者提交阶段,如果某个参与者回滚或者提交失败了,协调者可以采取一个定期重试来确保所有参与者回滚成功。
  • 同步阻塞问题:协调者必须等待所有参与者的 Yes/No 响应后,才能进入第二阶段;参与者必须等待协调者的 global commit/global rollback 请求后才能进入第二阶段;如果协调者出现宕机了,那么所有参与者都无法释放事务资源。
  • 单点问题/脑裂:整个分布式事务驱动都是基于协调者去做的,如果协调者出现问题,整个过程不可用。

3PC

三阶段提交是为了解决二阶段提交的 数据不一致、同步阻塞、单点等问题。

引入了超时机制

  • 参与者在等待协调者请求超时后,允许执行默认的操作;
  • 协调者在等待参与者响应超时后,允许执行默认的操作(发送中断事务)。

降低了事务资源的锁定范围

  • 新增了 CanCommit 阶段,不会像 2PC 一样一开始就锁定所有事务资源,而是排除掉个别不具备处理事务能力的参与者的前提下,再进入二阶段。

协商过程

img

区别于 2PC,只是在第一步先由协调者向所有参与者发送 CanCommit 请求,根据参与者的响应排除一些不具备事务处理能力的参与者,后续阶段和 2PC 相同。

3PC 相比 2PC 的优化

  • 增加了 CanCommit 阶段来减少事务资源的锁定范围,排除掉一些不具备事务资源能力的参与者;
  • 降低了同步阻塞,分别在参与者等待协调者请求、协调者在等待参与者响应 两个阶段新增超时机制。比如说参与者在等待协调者响应超时后默认执行提交操作,因为经过 CanCommit 阶段后事务成功提交的可能性已经很大了。

3PC 引入的问题

  • 新增一轮消息,增加了复杂度和协商效率;
  • 数据不一致:如果第二阶段结束后,一部分参与者能够执行事务反馈 Yes,另一部分参与者不能执行事务反馈 No,此时协调者恰好宕机,那么参与者会因为等待超时而自动执行默认操作,这就导致了部分事务成功提交、部分事务回滚,产生数据不一致的问题。
posted @ 2024-04-29 16:29  Stitches  阅读(17)  评论(0编辑  收藏  举报