分布式事务解决方案

要开始实现分布式事务,我们得先从理论上开始下手,我们来了解一下常用的分布式事务解决方案。

  1. XA分布式事务协议 - 2PC(两阶段提交实现)

    这里的PC实际上指的是Prepare和Commit,也就是说它分为两个阶段,一个是准备一个是提交,整个过程的参与者一共有两个角色,一个是事务的执行者,一个是事务的协调者,实际上整个分布式事务的运作都需要依靠协调者来维持:

    image-20220331214050440

    在准备和提交阶段,会进行:

    • 准备阶段:

      一个分布式事务是由协调者来开启的,首先协调者会向所有的事务执行者发送事务内容,等待所有的事务执行者答复。

      各个事务执行者开始执行事务操作,但是不进行提交,并将undo和redo信息记录到事务日志中。

      如果事务执行者执行事务成功,那么就告诉协调者成功Yes,否则告诉协调者失败No,不能提交事务。

    • 提交阶段:

      当所有的执行者都反馈完成之后,进入第二阶段。

      协调者会检查各个执行者的反馈内容,如果所有的执行者都返回成功,那么就告诉所有的执行者可以提交事务了,最后再释放锁资源。

      如果有至少一个执行者返回失败或是超时,那么就让所有的执行者都回滚,分布式事务执行失败。

    虽然这种方式看起来比较简单,但是存在以下几个问题:

    • 事务协调者是非常核心的角色,一旦出现问题,将导致整个分布式事务不能正常运行。
    • 如果提交阶段发生网络问题,导致某些事务执行者没有收到协调者发来的提交命令,将导致某些执行者提交某些执行者没提交,这样肯定是不行的。
  2. XA分布式事务协议 - 3PC(三阶段提交实现)

    三阶段提交是在二阶段提交基础上的改进版本,主要是加入了超时机制,同时在协调者和执行者中都引入了超时机制。

    三个阶段分别进行:

    • CanCommit阶段:

      协调者向执行者发送CanCommit请求,询问是否可以执行事务提交操作,然后开始等待执行者的响应。

      执行者接收到请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态,否则返回No

    • PreCommit阶段:

      协调者根据执行者的反应情况来决定是否可以进入第二阶段事务的PreCommit操作。

      如果所有的执行者都返回Yes,则协调者向所有执行者发送PreCommit请求,并进入Prepared阶段,执行者接收到请求后,会执行事务操作,并将undo和redo信息记录到事务日志中,如果成功执行,则返回成功响应。

      如果所有的执行者至少有一个返回No,则协调者向所有执行者发送abort请求,所有的执行者在收到请求或是超过一段时间没有收到任何请求时,会直接中断事务。

    • DoCommit阶段:

      该阶段进行真正的事务提交。

      协调者接收到所有执行者发送的成功响应,那么他将从PreCommit状态进入到DoCommit状态,并向所有执行者发送doCommit请求,执行者接收到doCommit请求之后,开始执行事务提交,并在完成事务提交之后释放所有事务资源,并最后向协调者发送确认响应,协调者接收到所有执行者的确认响应之后,完成事务(如果因为网络问题导致执行者没有收到doCommit请求,执行者会在超时之后直接提交事务,虽然执行者只是猜测协调者返回的是doCommit请求,但是因为前面的两个流程都正常执行,所以能够在一定程度上认为本次事务是成功的,因此会直接提交)

      协调者没有接收至少一个执行者发送的成功响应(也可能是响应超时),那么就会执行中断事务,协调者会向所有执行者发送abort请求,执行者接收到abort请求之后,利用其在PreCommit阶段记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源,执行者完成事务回滚之后,向协调者发送确认消息, 协调者接收到参与者反馈的确认消息之后,执行事务的中断。

    相比两阶段提交,三阶段提交的优势是显而易见的,当然也有缺点:

    • 3PC在2PC的第一阶段和第二阶段中插入一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的。
    • 一旦参与者无法及时收到来自协调者的信息之后,会默认执行Commit,这样就不会因为协调者单方面的故障导致全局出现问题。
    • 但是我们知道,实际上超时之后的Commit决策本质上就是一个赌注罢了,如果此时协调者发送的是abort请求但是超时未接收,那么就会直接导致数据一致性问题。
  3. TCC(补偿事务)

    TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作 :预处理Try、确认Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作既回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。

    img

    分支事务失败的情况 :

    img

    TCC分为三个阶段 :

    1. Try阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm一起才能真正构成一个完整的业务逻辑。
    2. Confirm阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行Confirm。通常情况下,采用TCC则认为Confirm阶段是不会出错的。即 :只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
    3. Cancel阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。
    4. TM事务管理器 TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公用组件,是为了考虑结构和软件复用。 TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文,追踪和记录状态,由于Confirm和Cancel失败需进行重试,因此需要实现为幂等性是指同一个操作无论请求多少次,其结果都相同。