事务 - 2PC

2PC

githubgit

上一篇文章中咱们介绍了本地事务,随着软件复杂度的上升,咱们会须要一种能够在多个数据库之间完成事务(分布式事务)的方法,而这个方法也必须可以保证ACID。因而就出现了2PC - Two phase commit protocol。事实上2PC不单单适用于多数据库事务场景下使用,也适用于全部支持2PC的参与方(Participants)。github

算法介绍

2PC的参与方有:算法

  1. 一个做为Coordinator的节点
  2. 多个做为Cohort的网络节点

2PC假设:数据库

  1. 全部节点都有一个稳定存储用以保存WAL(write-ahead log
  2. 没有一个节点会永远崩溃(即会最终恢复)
  3. write-ahead log中存储的数据永远不会丢失,不会因崩溃而损坏
  4. 任意两个节点都可以互相通讯

第四个假设太过严格,实际上有不是全部的2PC实现都知足。第1、二个假设则大多数2PC实现都能知足。segmentfault

PS. 若是某个节点彻底损坏(好比服务器物理损毁),那么数据就直接丢失了。服务器

2PC的执行步骤:网络

  1. Commit request phase /Voting phase。这个阶段作:并发

    1. Coordinator发送一个查询是否赞成commit的请求到全部Cohort,而且等待全部Cohort给出应答。
    2. Cohort收到请求,开始执行事务,执行到就差commit为止(不commit)。
    3. 每一个Cohort根据操做结果返回Yes或No
  2. Commit phase / Completion phase。这个阶段分两种状况:分布式

    1. 成功。全部Cohort应答Yeside

      1. Coordinator发送commit指令到全部Cohort
      2. 每一个Cohort执行commit,并发送ack到Coordinator
      3. 当Coordinator收到每一个Cohort的ack以后则事务完成
    2. 失败。任意Cohort应答No,或者在commit request阶段超时

      1. Coordinator发送rollback指令到全部Cohort
      2. 每一个Cohort执行rollback,并发送ack到Coordinator
      3. 当Coordinator收到每一个Cohort的ack以后则事务撤销

消息流(摘自wiki):

Coordinator                                         Cohort
                              QUERY TO COMMIT
                -------------------------------->
                              VOTE YES/NO           prepare*/abort*
                <-------------------------------
commit*/abort*                COMMIT/ROLLBACK
                -------------------------------->
                              ACKNOWLEDGMENT        commit*/abort*
                <--------------------------------  
end

2PC的通讯次数是:

  1. 若是实现没有要求任意两个Cohort能够通讯,那么是2n(n=Cohort数量)
  2. 若是实现要求任意两个Cohort能够通讯,那么是n^2

异常处理

咱们把上面的流程简化以便说明异常处理:

  1. Coordinator发送query to commit
  2. Cohort执行prepare
  3. Cohort返回ack
  4. Coordinator发送commit/rollback
  5. Cohort执行commit/rollback
  6. Cohort返回ack

从Coordinate角度来看出现异常要怎么处理:

  1. step 1发生异常,Coordinator须要执行rollback
  2. step 二、3发生异常,意味着Coordinator没有收到Cohort的响应,这个时候因认定为失败,执行rollback
  3. step 4发生异常,Coordinator重试commit/rollback
  4. step 五、6发生异常,意味着Coordinator没有收到Cohort的响应,这个时候因认定为失败,重试commit/rollback

从Cohort角度来看看看出现异常怎么处理:

  1. step 1,意味着Cohort没有收到请求,什么都不须要作
  2. step 2,意味着Cohort没有执行成功,什么都不须要作
  3. step 3,意味着Coordinator没有收到结果,什么都不须要作,等待Coordinator重试便可。Cohort要保证prepare是幂等的。
  4. step 4,等待Coordinator重试便可,这里有点tricky,若是Coordinator迟迟不retry,那么Cohort要自行rollback,不然就会形成资源死锁。
  5. step 5,等待Coordinator重试便可
  6. step 6,意味着Coordinator没有收到结果,什么都不须要作,等待Coordinator重试便可,Cohort要保证commit/rollback是幂等的。

观察一下你就会发现依然存在漏洞——会出现违反一致性的状况:

  1. 若Coordinator/Cohort因崩溃遗失了信息,有的Cohort已commit,有的Cohort则恢复到commit以前的状态。
  2. 若Coordinator在step 4发送commit,而Cohort在rollback(因timeout致使的rollback)。

出现上面的状况就须要人工介入了。

更多2PC的异常处理推理详见这篇slides

缺点

根据上面的算法介绍能够看出2PC是一个阻塞协议:

  • 若是两个事务针对同一个数据,那么后面的要等待前面完成,这是因为Cohort采用的是本地事务所决定的
  • Cohort在commit request phase以后会阻塞,直到进入Coordinator告之Cohort进入commit phase

对于ACID的保证

2PC所保证的ACID和本地事务所提到的ACID不太同样——事实上对于全部分布式事务来讲都不太同样:

  • A,正常状况下保证
  • C,在某个时间点,会出现A库和B库的数据违反一致性要求的状况
  • I,在某个时间点,A事务可以读到B事务部分提交的结果
  • D,和本地事务同样,只要commit则数据被持久

XA

XA是一个针对分布式事务的spec,它实现了2PC协议。在XA中定义了两种参与方:Transaction Manager(TM)和Resource Manager(RM),其中TM=2PC中的Coordinator,RM=2PC中的Cohort。

Java规范中的JTA(Java Transaction API)定义了XA的Java接口,JTA的实现有Bitronix、Atomikos等等。

参考资料

相关文章
相关标签/搜索