2PC
githubgit
在上一篇文章中咱们介绍了本地事务,随着软件复杂度的上升,咱们会须要一种能够在多个数据库之间完成事务(分布式事务)的方法,而这个方法也必须可以保证ACID。因而就出现了2PC - Two phase commit protocol。事实上2PC不单单适用于多数据库事务场景下使用,也适用于全部支持2PC的参与方(Participants)。github
算法介绍
2PC的参与方有:算法
- 一个做为Coordinator的节点
- 多个做为Cohort的网络节点
2PC假设:数据库
- 全部节点都有一个稳定存储用以保存WAL(write-ahead log)
- 没有一个节点会永远崩溃(即会最终恢复)
- write-ahead log中存储的数据永远不会丢失,不会因崩溃而损坏
- 任意两个节点都可以互相通讯
第四个假设太过严格,实际上有不是全部的2PC实现都知足。第1、二个假设则大多数2PC实现都能知足。segmentfault
PS. 若是某个节点彻底损坏(好比服务器物理损毁),那么数据就直接丢失了。服务器
2PC的执行步骤:网络
-
Commit request phase /Voting phase。这个阶段作:并发
- Coordinator发送一个查询是否赞成commit的请求到全部Cohort,而且等待全部Cohort给出应答。
- Cohort收到请求,开始执行事务,执行到就差commit为止(不commit)。
- 每一个Cohort根据操做结果返回Yes或No
-
Commit phase / Completion phase。这个阶段分两种状况:分布式
-
成功。全部Cohort应答Yeside
- Coordinator发送commit指令到全部Cohort
- 每一个Cohort执行commit,并发送ack到Coordinator
- 当Coordinator收到每一个Cohort的ack以后则事务完成
-
失败。任意Cohort应答No,或者在commit request阶段超时
- Coordinator发送rollback指令到全部Cohort
- 每一个Cohort执行rollback,并发送ack到Coordinator
- 当Coordinator收到每一个Cohort的ack以后则事务撤销
消息流(摘自wiki):
Coordinator Cohort
QUERY TO COMMIT
-------------------------------->
VOTE YES/NO prepare*/abort*
<-------------------------------
commit*/abort* COMMIT/ROLLBACK
-------------------------------->
ACKNOWLEDGMENT commit*/abort*
<--------------------------------
end
2PC的通讯次数是:
- 若是实现没有要求任意两个Cohort能够通讯,那么是2n(n=Cohort数量)
- 若是实现要求任意两个Cohort能够通讯,那么是n^2
异常处理
咱们把上面的流程简化以便说明异常处理:
- Coordinator发送query to commit
- Cohort执行prepare
- Cohort返回ack
- Coordinator发送commit/rollback
- Cohort执行commit/rollback
- Cohort返回ack
从Coordinate角度来看出现异常要怎么处理:
- step 1发生异常,Coordinator须要执行rollback
- step 二、3发生异常,意味着Coordinator没有收到Cohort的响应,这个时候因认定为失败,执行rollback
- step 4发生异常,Coordinator重试commit/rollback
- step 五、6发生异常,意味着Coordinator没有收到Cohort的响应,这个时候因认定为失败,重试commit/rollback
从Cohort角度来看看看出现异常怎么处理:
- step 1,意味着Cohort没有收到请求,什么都不须要作
- step 2,意味着Cohort没有执行成功,什么都不须要作
- step 3,意味着Coordinator没有收到结果,什么都不须要作,等待Coordinator重试便可。Cohort要保证prepare是幂等的。
- step 4,等待Coordinator重试便可,这里有点tricky,若是Coordinator迟迟不retry,那么Cohort要自行rollback,不然就会形成资源死锁。
- step 5,等待Coordinator重试便可
- step 6,意味着Coordinator没有收到结果,什么都不须要作,等待Coordinator重试便可,Cohort要保证commit/rollback是幂等的。
观察一下你就会发现依然存在漏洞——会出现违反一致性的状况:
- 若Coordinator/Cohort因崩溃遗失了信息,有的Cohort已commit,有的Cohort则恢复到commit以前的状态。
- 若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等等。
参考资料