分布式事务

分布式之分布式事务

2. 分布式事务

假设多个服务要触发一系列连续的操作,每个操作涉及到不同的数据库,且这一套操作要么全部成功,要么全部失败,那么分布式事务就是保证这一套发生于不同服务的、涉及不同数据库的操作是一个事务操作,构成一个全局事务

假设你要创建一个申请购买电脑的工单,存在工单处理、资产审批、资产购买这三个微服务,且对应三个库,那么就必须使得下面一套流程遵循事务特性:修改审批状态为通过->修改资产购买状态为完成->修改工单状态为结束

2.1 两阶段提交(2 Phase Commit)

2.1.1 定义

通过引入单点协调者来协调多个需要一次完成的多个事务,通过协调者保证这一套事务的成功提交

2.1.2 流程

  • 投票阶段

    • 协调者向所有参与者发出投票请求
    • 参与者收到后开始执行事务,但不提交,如果
      • 执行成功,则返回可以提交
      • 执行失败,则返回需要回滚
  • 提交阶段

    协调者如果

    • 收到所有反馈,且

      • 全为成功,则发出提交请求至参与者,参与者收到后进行commit,并返回ack
      • 不全为成功,则发出回滚请求至参与者,参与者收到后进行rollback,并返回ack
    • 在规定时间内未收到所有反馈

      向所有参与者发出回滚请求,参与者收到后进行rollback,并返回ack

2.1.3 故障分析

  • 只有参与者宕机

    • 在投票阶段宕机(非阻塞)

      协调者会超时,之后让其他协调者rollback

    • 在提交阶段宕机(非阻塞)

      由于协调者会将二阶段做出的决策写入日志,当此参与者恢复时,可以通过询问协调者得到此决策,来决定时回滚还是提交,注意,这里协调者不会阻塞等待参与者恢复

  • 只有协调者宕机

    • 在投票阶段宕机(非阻塞)

      所有参与者会推选出新的协调者

    • 在提交阶段宕机

      • 如果没有任何参与者收到协调者的决策

        所有参与者推选出新的协调者来重启2PC

      • 如果有参与者收到了协调者的决策(非阻塞)

        收到信息的参与者可以将决策传播给其他参与者,完成提交或回滚

  • 参与者和协调者均宕机

    • 均在第一阶段宕机(非阻塞)

      剩余的参与者推选新的协调者

    • 参与者第一阶段宕机&协调者第二阶段宕机

      • 如果没有任何剩余活跃参与者收到协调者的决策(非阻塞)

        所有活跃参与者推选出新的协调者来重启2PC

      • 如果有剩余活跃参与者收到了协调者的决策(非阻塞)

        收到信息的参与者可以将决策传播给其他参与者,完成提交或回滚

    • 参与者第二阶段宕机&协调者第一阶段宕机

      不存在

    • 均在第二阶段宕机(阻塞!!!

      这里考虑下面时序:

      协调者向参与者A发出提交请求 -> 协调者宕机 -> 参与者A收到提交请求并写盘 -> 参与者A宕机

      那么此时如果剩余的活跃的参与者推选出新的协调者,决定回滚, 之后原协调者恢复发出提交,这样两者就产生冲突!

      上面时序尽管苛刻,但是对于参与者和协调者均在第二阶段宕机这个情况,从剩余活跃的参与者角度来看,并不清楚

      • 是否协调者发出提交/回滚请求
      • 是否宕机的参与者收到了提交/回滚请求

      这样,剩余的参与者只能等待协调者恢复

2.1.4 缺点

  • 单点故障

    如果协调者单点出现问题,那么整个事务就失去了原子性保证,可以通过推选新的协调者来重新执行一次2PC流程

  • 同步阻塞

    • 2PC整个阶段,协调者和参与者都要保持同步
    • 故障分析中最后一个例子,会使得所有活跃的参与者陷入阻塞状态

2.2 三阶段提交(3 Phase Commit)

2.2.1 定义

同样使用单点协调者,相较于2PC引入了多了一个阶段,用于解决2PC中协调者第二阶段宕机,所有参与者阻塞的问题

2.2.2 流程

  • CanCommit阶段

    • 协调者发出CanCommit请求

    • 参与者收到后,尝试获取数据库锁,检查执行事务所需资源是否就绪,如果

      • 就绪

        返回ack

      • 未就绪

        返回nack

  • PreCommit阶段

    • 协调者如果收到所有ack,则发出预提交请求

    • 参与者收到后,执行事务,但不提交,如果

      • 执行成功

        返回ack

      • 执行失败

        返回nack

  • DoCommit阶段

    • 协调者如果收到事务执行结果均为成功

      发出commit请求,所有参与者收到后commit,并ack

    • 存在失败

      发出rollback请求,所有参与者收到后rollback,并ack

2.2.3 故障分析

这里仅对协调者分析

  • CanCommit阶段宕机

    参与者重选协调者

  • PreCommit阶段宕机

    代表所有参与者状态正常,且没有或只有一部分参与者执行了事务,这时参与者会推选新的协调者接着此阶段继续执行

  • doCommit阶段宕机

    所有参与者此时处于执行事务但未提交状态,只要有任意参与者收到commit则进行提交,如果没有参与者收到,则超时后进行提交

2.2.4 为何3PC可以解决2PC问题

2PC的问题在于,参与者不清楚协调者的状态,只有协调者直到所有参与者的状态。而3PC通过将2PC的第一阶段拆分成CanCommit和PreCommit,之后由CanCommit保证协调者预备状态良好,由PreCommit告知参与者协调者意欲完成这次提交,这样就可以让参与者遇到协调者

  • 在第三阶段宕机时可以有充分保证在超时状态下提交
  • 在第二阶段宕机时,此时未进行提交,可以重选协调者继续流程

2.2.5 缺点

  • 在协调者宕机时,由于网络分片问题,可以会推举出多个新的协调者,当时数据不一致
  • 多了一个阶段,使得网络延迟增大,使得全局事务阻塞时间也增大

2.3 TCC(Try Confirm Cancel)

这里摘抄周志明老师的原文

"整体流程类似于2PC,但 TCC 是位于用户代码层面,而不是在基础设施层面,这为它的实现带来了较高的灵活性,可以根据需要设计资源锁定的粒度"

—————分布式事务 | 凤凰架构 (icyfenix.cn)

2.4 其他方案

还有本地消息表、基于消息队列和尽最大努力通知三个手段可以参见

面试必问:分布式事务六种解决方案 - 知乎 (zhihu.com)

分布式事务 | 凤凰架构 (icyfenix.cn)

# 参考

分布式事务:两阶段提交与三阶段提交 - SegmentFault 思否

Two-phase commit protocol - Wikipedia

终于有人把“TCC分布式事务”实现原理讲明白了! - JaJian - 博客园 (cnblogs.com)

Lecture - 25 Basic 2-Phase and 3-phase commit protocol - YouTube

posted @ 2021-09-07 16:55  Linus1  阅读(42)  评论(0编辑  收藏  举报