您的位置:首页 > 其它

事务与两阶段提交

2015-01-25 20:41 106 查看

事务

事务是保证数据库从一个一致性的状态永久性地变成另一个一致性状态的根基

ACID

ACID是事务基本特性:

A是Atomicity,原子性。一个事务往往涉及到许多的子操作,原子性则保证这些子操作要么都做,要么都不做,不能出现部分操作成功,而另外一部分操作失败情形,基于此原则,如果事务在执行的中发生异常,那么数据库将回滚到事务发生之前的状态。

C是Consistency,一致性。一致性是指事务发生前和发生后,都不会破坏原有的约束关系,保证了数据库元素的正确性、有效性和完整性,其中的约束关系可以是数据库内部的约束,比如数据库元素的值必须在一定的范围内,也可以是应用带来的约束,比如银行账户的余额不能为负数。

I是Isolation,隔离性。一个事务的操作在提交之前,是不会被并行发生的其他事务访问到的,也就是说,数据库操作不会看到某个事务的中间操作结果,比如转账过程中,用户是不能查询到一个账户余额减少了,而另外一个账户余额未发生变化的情况。

D是Durability,持久性。事务完成以后,它对数据库的影响是永久性的,即使在数据库系统发生宕机或者其他故障的情况下,这种影响也会得到保持。

两阶段提交

两阶段提交机制是分布式事务实现的关键,在分布式系统中,事务往往包含有多个参与者,单个参与者上的事务能够保证自身的ACID,而多个参与者之间的事务ACID则需要两阶段提交机制来保证,许多分布式关系型数据管理系统采用此机制来完成分布式事务。

两阶段提交机制依赖两阶段提交协议(two phase commit protocol,2PC),两阶段提交协议通过协调事务的参与者,决定提交或取消(回滚)事务,它能够解决很多的临时性系统故障(包括进程、网络节点、通信等故障)问题,被广泛地使用,它解决临时性系统故障是通过使用日志来记录协议的状态,从而使节点在故障恢复后可以继续进行交易。两阶段提交协议并不能够解决所有的故障,在某些情况下它还需要人为的参与才能解决问题。

在两阶段提交协议中,系统一般包含两类节点:一类为协调者,通常一个系统中只有一个;另一类为事务参与者,一般包含多个,协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,这样,即使节点发生故障日志也不会丢失,协议中也假设节点不会发生永久性故障而且任意两个节点都可以互相通信

两阶段提交,顾名思义,由两个阶段组成:

阶段1:请求阶段(commit-request phase,或称表决阶段,voting phase)

在这个阶段,协调者在自身节点的日志中写入一条的日志记录,然后所有参与者发送消息prepare T,询问这些参与者是否能够提交这个事务;

参与者在接受到这个prepare T 消息以后,会根据自身的情况,进行事务的预处理,

如果参与者能够提交该事务,则会将日志写入磁盘,并返回给协调者一个ready T信息,同时自身进入预提交状态状态;

如果不能提交该事务,则记录日志,并返回一个not commit T信息给协调者,同时撤销在自身上所做的数据库改;

参与者可以推迟发送响应的时间,但最终还是需要发送的。

阶段2:提交阶段(commit phase)

在这个阶段,协调者会收集所有参与者的意见,

如果收到有参与者发来not commit T信息,则标识着该事务不能提交,协调者会将Abort T 记录到日志中,并向所有参与者发送一个Abort T 信息,让所有参与者撤销在自身上所有的预操作;

如果协调者收到所有参与者发来ready T信息,那么协调者会将Commit T日志写入磁盘,并向所有参与者发送一个Commit T信息,提交该事务。

若协调者迟迟未收到某个参与者发来的信息,则认为该参与者发送了一个VOTE_ABORT信息,从而取消该事务的执行。

如果参与者接收到协调者发来的Abort T信息以后,将Abort T 记录到日志中,会终止提交;

如果参与者收到的是Commit T信息,则写入记录,将事务进行提交。

两阶段提交机制图示:



一般情况下,两阶段提交机制都能较好的运行,当在事务进行过程中,有参与者宕机时,他重启以后,可以通过询问其他参与者或者协调者,从而知道这个事务到底提交了没有,当然,这一切的前提都是各个参与者在进行每一步操作时,都会事先写入日志。

两阶段提交存在的不能解决的困境:当协调者在发出commit T消息后宕机了,而唯一收到这条命令的一个参与者也宕机了,这个时候这个事务就处于一个未知的状态,没有人知道这个事务到底是提交了还是未提交,从而需要数据库管理员的介入,防止数据库进入一个不一致的状态。

当然,如果基于前面的假设即所有节点或者网络的异常最终都会恢复,那么这个问题就不存在了,协调者和参与者最终会重启,其他节点也最终也会收到commit T的信息,这就是基于日志完成的最终一致性

缺陷

两阶段提交协议最大的劣势是其依赖“阻塞”完成,在节点等待消息的时候处于阻塞状态,节点中其他进程则需要等待阻塞进程释放资源才能使用。另外,两阶段提交协议没有容错机制,一个节点发生故障整个事务都要回滚,代价比较大,这需要我们通过其它手段来完成容错。

一个例子

一个例子来说明两阶段提交协议的工作过程:

A组织B、C和D三个人去爬长城,如果所有人都同意去爬长城,那么活动将举行,如果有一人不同意去爬长城,那么活动将取消。

用2PC算法解决该问题的过程如下:

角色划分:A是活动的协调者,B、C和D是活动的参与者。

阶段1:

A发邮件给B、C和D,提出下周三去爬山,问是否同意。此时A需要等待B、C和D的回复邮件。

B、C收到邮件后发现自己在当日没有活动安排,则发邮件告诉A他们同意下周三去爬长城。由于某种原因,D白天没有查看邮件,此时A、B和C均需要等待。到晚上的时候,D发现了A的邮件,然后查看日程安排,发现周三当天已经有别的安排,那么D回复A说我去不了,至此第一阶段的回复完成。

阶段2:

此时A收到了所有活动参与者的邮件,如果A发现三人都同意,则发下一封邮件通知三人说,活动确定了,下周三去爬山,事务完成,结束。

可是A发现D下周三不能去爬山,那么A将发邮件通知B、C和D,下周三爬长城活动取消,

此时B、C回复A“太可惜了”,D回复A“不好意思”,至此该事务回滚,结束。

上面的例子中,A一共经历了两次发送邮件并接受全部回复的过程,这就是两阶段提交名字的由来。

通过该例子可以发现,2PC协议存在的缺陷。假如D不回复邮件,那么A、B和C将不得不处于等待的状态。并且B和C所持有的资源,即下周三不能安排其它活动,一直不能释放,其它等待该资源释放的活动也将不得不处于等待状态。

鉴于此问题,后来引入超时的机制,在超时发生以前,系统处于不确定阶段;在超时发生以后,系统则转入确定阶段。

两阶段提交协议包含协调者和参与者,且二者都有发生问题的可能性。

假如协调者发生问题,我们可以选出另一个协调者来提交事务,例如,班长组织活动,如果班长生病了,我们可以请副班长来组织,这样协调者出问题,那么事务将不会取消或超时,如果班级活动希望每个人都能去,假如有一位同学不能去了,那么直接取消活动即可,或者,如果大多数人去的话那么活动如期举行(2PC变种),

为了能够更好地解决实际的问题,2PC协议存在很多的变种,例如:树形2PC协议(或称递归2PC协议)、动态2阶段提交协议(D2PC)等。

参考:
http://www.blogjava.net/freeman1984/archive/2011/10/09/360269.html http://blog.csdn.net/shenlan211314/article/details/7283948
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐