分布式之分布式事务

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 是位于用户代码层面,而不是在基础设施层面,这为它的实现带来了较高的灵活性,可以根据需要设计资源锁定的粒度"

​​