二阶段提交算法与paxos算法

1. 二阶段提交算法

1.1 算法描述:

       在分布式系统中,事务每每包含有多个参与者的活动,单个参与者上的活动是可以保证原子性的,而多个参与者之间原子性的保证则须要经过两阶段提交来实现,两阶段提交是分布式事务实现的关键。web

  很明显,两阶段提交保证了分布式事务的原子性,这些子事务要么都作,要么都不作。而数据库的一致性是由数据库的完整性约束实现的,持久性则是经过commit日志来实现的,不是由两阶段提交来保证的。至于两阶段提交如何保证隔离性,能够参考Large-scale Incremental Processing Using Distributed Transactions and Notifications中两阶段提交的具体实现。算法

  两阶段提交的过程涉及到协调者和参与者。协调者能够看作成事务的发起者,同时也是事务的一个参与者。对于一个分布式事务来讲,一个事务是涉及到多个参与者的。具体的两阶段提交的过程以下: 
第一阶段: 
  首先,协调者在自身节点的日志中写入一条的日志记录,而后全部参与者发送消息prepare T,询问这些参与者(包括自身),是否可以提交这个事务; 
  参与者在接受到这个prepare T 消息之后,会根据自身的状况,进行事务的预处理,若是参与者可以提交该事务,则会将日志写入磁盘,并返回给协调者一个ready T信息,同时自身进入可提交状态;若是不能提交该事务,则记录日志,并返回一个not commit T信息给协调者,同时撤销在自身上所作的数据库改; 
第二阶段: 
  协调者会收集全部参与者的意见。(1)若是收到参与者发来的not commit T信息,则标识着该事务不能提交,协调者会将Abort T 记录到日志中,并向全部参与者发送一个Abort T 信息,让全部参与者撤销在自身上全部的预操做;(2)若是协调者收到全部参与者发来prepare T信息,那么协调者会将Commit T日志写入磁盘,并向全部参与者发送一个Commit T信息,提交该事务。(3)若协调者迟迟未收到某个参与者发来的信息,则认为该参与者发送了一个VOTE_ABORT信息,从而取消该事务的执行。 
  参与者接收到协调者发来的Abort T信息之后,参与者会终止提交,并将Abort T 记录到日志中;若是参与者收到的是Commit T信息,则会将事务进行提交,并写入记录。数据库

1.2 二阶段提交算法存在的问题:

通常状况下,两阶段提交机制都能较好的运行,但可能出现下面三种问题: 安全

  1. 协调者不宕机,参与者宕机; 
  2. 协调者宕机,参与者不宕机; 
  3. 协调者宕机,参与者也宕机; 

  对于1,当在事务进行过程当中,有参与者宕机时,他重启之后,能够经过询问其余参与者或者协调者,从而知道这个事务到底提交了没有。固然,这一切的前提都是各个参与者在进行每一步操做时,都会事先写入日志。 
  对于2,协调者宕机后,能够起新的协调者,而后查询全部参与者的状态是否有commit的,若是有,则继续commit,若是都没有,则abort。 
  对于3,是惟一一个两阶段提交不能解决的困境是:当协调者在发出commit T消息后宕机了,而惟一收到这条命令的一个参与者也宕机了,这个时候这个事务就处于一个未知的状态,没有人知道这个事务究竟是提交了仍是未提交,从而须要数据库管理员的介入,防止数据库进入一个不一致的状态。固然,若是有一个前提是:全部节点或者网络的异常最终都会恢复,那么这个问题就不存在了,协调者和参与者最终会重启,其余节点也最终也会收到commit T的信息。 
  对于上面的困境,业界提出了三阶段提交的方法来此问题,即将二阶段提交的第二阶段再分为待定阶段(或预提交阶段)和肯定阶段,从而变为三阶段;在待定阶段协调者log prepare_commit消息后向全部参与者发送prepare_commit消息, 待收到全部参与者回包(这里的回包结果只会成功)或超时时就进入第三段阶,log commit消息并向全部参与者发送commit消息。若是在待定阶段和肯定阶段出现协调者和部分参与者同时宕机(即上面的困境),只要存活的协调者或参与者里有prepare_commit或commit消息,新的协调者能够继续进行commit消息,若是没有,就不commit消息,从而保证数据的一致性。服务器

1.3 总结

二阶段提交和三阶段提交都是很好的分布式事务算法,三阶段提交是为解决二阶段提交未解决的问题(协调者宕机,参与者也宕机)而提出来的。不过这两种算法都只考虑一个协调者(主节点)的状况,没有考虑多个协调者和如何选出协调者的问题。而另外一种知名分布式事务算法pasox能解决多个协调者的状况,并提出了多数派的概念。网络

2. pasox算法

2.1 背景

Paxos算法是Lamport于1990年提出的一种基于消息传递的一致性算法。因为算法难以理解起初并无引发人们的重视,使Lamport在八年后从新发表到TOCS上。即使如此paxos算法仍是没有获得重视,2001年Lamport用可读性比较强的叙述性语言给出算法描述。可见Lamport对paxos算法情有独钟。近几年paxos算法的广泛使用也证实它在分布式一致性算法中的重要地位。06年google的三篇论文初现“云”的端倪,其中的chubby锁服务使用paxos做为chubby cell中的一致性算法,paxos的人气今后一路狂飙。分布式

2.2 Paxos是什么

Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。一个典型的场景是,在一个分布式数据库系统中,若是各节点的初始状态一致,每一个节点都执行相同的操做序列,那么他们最后能获得一个一致的状态。为保证每一个节点执行相同的命令序列,须要在每一条指令上执行一个“一致性算法”以保证每一个节点看到的指令一致,是分布式计算中的重要问题。学习

2.3 Paxos的两个原则

安全原则---保证不能作错的事ui

1. 只能有一个值被批准,不能出现第二个值把第一个覆盖的状况google

2. 每一个节点只能学习到已经被批准的值,不能学习没有被批准的值

存活原则---只要有多数服务器存活而且彼此间能够通讯最终都要作到的事

1. 最终会批准某个被提议的值

2. 一个值被批准了,其余服务器最终会学习到这个值

2.4 Paxos的两个组件

Proposer

提议发起者,处理客户端请求,将客户端的请求发送到集群中,以便决定这个值是否能够被批准。

Acceptor

提议批准者,负责处理接收到的提议,他们的回复就是一次投票。会存储一些状态来决定是否接收一个值

2.5 Paxos定义

首先从最简单的方式开始,假设只有一个Acceptor,多个Proposer,让它作决定是否批准一个值

每个proposer提议一个值给Acceptor来批准,而后Acceptor批准一个值做为最终的值。

如何保证一致性?使用互斥(锁),只有一个proposer可以获得锁,一旦值被写入,后面获得锁的proposer没法改变值。

可是若是某个proposer得到锁之后,在赋值前down掉了,尚未释放锁资源,那么此时产生了死锁。

2.5.1 抢占式

为了解决死锁问题,引入了proposer抢占式,

acceptor可让某个proposer的访问失效,再也不接收它的访问。以后能够将访问权发放给其余proposer。

proposer向acceptor申请访问权时指定编号epoch(越大越新,可使用当前时间做为epoch),得到访问权之后才能向acceptor提交新值。

acceptor采用“喜新厌旧”原则,一旦更大的epoch申请访问,立刻让旧的epoch访问失效,再也不接收他们提交的取值。而后给新的epoch发放访问权限,接受新的epoch的取值。

新的epoch能够抢占旧epoch,让旧epoch访问失效,旧epoch的proposer将没法进行,新epoch的proposer将开始运行。

为了保证一致性,不一样epoch的proposer采用“后者认同前者”的原则:

在确定旧epoch没法生成肯定性取值时,新的epoch会提交本身的value,不会冲突。

一旦旧epoch造成肯定性取值,新的epoch确定能够得到此取值,而且会认同此取值,不会破坏。

这样就解决了死锁问题。

2.5.2 多个Acceptor

paxos算法就是在抢占式的基础上引入多个acceptor,acceptor的实现保持不变,仍采用“喜新厌旧”的原则运行。

paxos采用“少数服从多数“原则,一旦某个epoch的取值f被半数以上的acceptor接受,则认为确认了f,不能再被更改。

proposer第一阶段:

选定epoch,获取半数以上访问权,获取acceptor当前值。

proposer第二阶段:

若是得到的当前全部acceptor的值为null,则将本身的值v提交给全部获取访问权的acceptor,若是收到半数以上acceptor成功,则返回成功,不然失败(acceptor故障或者被新的epoch抢占)。

若是得到的当前acceptor中有某个有值,则认同其中最大的epoch提交的值f,若是此时f已是半数以上acceptor的值,那么返回成功。若是不是,则向全部得到访问权的acceptor提交f。

paxos算法能够知足容错需求,半数如下acceptor出现故障时,存活的acceptor仍然能够生成肯定性取值,一旦取值被肯定,即便半数如下acceptor故障,此取值能够被获取,而且将再也不被更改。

2.5.3 提议ID生成算法(epoch)

在Google的Chubby论文中给出了这样一种方法:假设有n个proposer,每一个编号为ir(0<=ir<n),proposor编号的任何值s都应该大于它已知的最大值,而且知足:s %n = ir => s = m*n + ir

proposer已知的最大值来自两部分:proposer本身对编号自增后的值和接收到acceptor的reject后所获得的值

以3个proposer P一、P二、P3为例,开始m=0,编号分别为0,1,2

1. P1提交的时候发现了P2已经提交,P2编号为1 > P1的0,所以P1从新计算编号:new P1 = 1*3+0 = 4

2. P3以编号2提交,发现小于P1的4,所以P3从新编号:new P3 = 1*3+2 = 5

 

 

Reference:

1. http://blog.csdn.net/whycold/article/details/47702133

2. http://www.tudou.com/programs/view/e8zM8dAL6hM/

3. http://mp.weixin.qq.com/s?__biz=MjM5MDg2NjIyMA==&mid=203607654&idx=1&sn=bfe71374fbca7ec5adf31bd3500ab95a&key=8ea74966bf01cfb6684dc066454e04bb5194d780db67f87b55480b52800238c2dfae323218ee8645f0c094e607ea7e6f&ascene=1&uin=MjA1MDk3Njk1&devicetype=webwx&version=70000001&pass_ticket=2ivcW%2FcENyzkz%2FGjIaPDdMzzf%2Bberd36%2FR3FYecikmo%3D

相关文章
相关标签/搜索