如何保证transaction commit的完整性

欢迎和我探讨数据库技术,可以给我发信:tieyingz@cs.cmu.edu


当commit涉及到多行的时候,并且提供的是行锁,那么就涉及到如何保证多行commit的完整性,不至于1行提交了,另外一行没有提交,那么concurrent txn会读到不同的结果。无论是单机事务还是分布式事务都涉及到这个问题。


如何解决上述问题?需要一个全局锁(确切的说应该叫公共flag),至于这个锁如何实现,不同数据库不同方法。例如Oracle对表进行加锁,这样多行commit完再对表锁进行释放。而对于分布式事务,则需要一个全局锁,因为涉及到多个节点。Oracle把全局锁放在了commit site上。我们可以想象成一个中心server上有一个meta表,一个txn访问数据的时候如果该数据已经写入了,但是version还是没有被置上,就去meta表里查看对应数据是否已经被标记成commit了。


因此要保证commit的原子性需要性能上的代价,特别是分布式事务中,会引来额外的RPC开销。如果上面提到的flag放在本地则会大大减少RPC带来的影响。草案如下:

有一个GlobalMetaTable,同时每个节点有LocalMetaTable。GlobalMeta针对GlobalTxn,LocalMeta针对LocalTxn和GlobalTxn。对于GlobalTxn,其commit开始时,首先获得GlobalCommitId,更新GlobalMetaTable对应的那条Tuple,然后同步其涉及到的所有节点的LocalMetaTable。


下面转了一个两阶段提交的过程,其特点是阻塞,来保证提交过程的原子性!

前提条件


系统节点分为:其中一个节点被设置为协调者(co-ordinator),其他节点设置为参与者(cohort)。

假定在每个节点上都有一个使用write-ahead log的稳定数据存储节点,说白了可以打日志,而且日志在persistent 存储中 。如果要成功,那么在收到commit前没有节点崩溃,write-log的日志保证不丢失,并且协调者和任意参与者都可以互相通信。所以如果要成功的话,条件还是很严格的。

算法

分两个阶段

提交请求阶段(或者叫做投票阶段)
    1.协调者发送一个query to commit消息给所有的cohorts,等待直到收到所有cohorts的回复。
    2.cohorts在本地节点执行事务(之后协调者会要求提交这个事务),写本地的redo和undo日志
    3.每一个cohorts,如果执行成功,回复一个agreement消息(假如cohorts同意执行commit);如果执行失败,回复一个abort消息。(两阶段提交协议)。

提交阶段(或者叫完成阶段)
成功
如果协调者接收到所有参与者发送回来的agreement消息:
    1.协调者发送一个commit消息给所有的cohorts
    2.每一个参与者完成commit操作,(两阶段提交协议)释放所有事务处理过程中使用的锁资源
    3.每一个参与者回复一个acknowledgment给协调者
    4.协调者在收到所有acknowledgment消息之后完成整个操作
失败
如果任何一个参与者在提交请求阶段回复abort消息给协调者:
    1.协调者回复一个rollback消息给所有的cohorts
    2.每一个参与者执行本地事务的undo操作(根据undo日志记录),并且释放事务执行过程中使用的资源和锁
    3.每一个参与者给协调者回复acknowledgement消息(两阶段提交协议)。
    4.协调者在接收到所有的参与者的acknowledgement消息之后执行事务undo操作

缺点
两阶段提交协议最大的缺点是:它是一个阻塞协议。当一个节点在等待回复消息时进入阻塞状态。其他需要这些资源的处理事务需要等待。如果协调者挂掉,cohorts将永远不能结束它们的事务,如下面的情况所述:
如果一个参与者发送agreement消息给协调者,它将进入阻塞状态直到收到回复的commit或者rollback 消息。如果这个时候协调者挂掉,并且不再恢复,这个cohort将一直阻塞(为什么这里不能使用等待超时的机制来abort掉这个事务呢?个人理解,如果一个参与者独自决定将这个未完成事务abort掉,可能导致全局的数据不一致,因为不知道其他节点是否执行了abort操作),(两阶段提交协议)除非它可以从其他cohort那里获得全局的commit/abort消息

当协调者发送query to commit消息之后,它将阻塞直到收到所有参与者的回复消息。如果这个时候,一个参与者挂掉,并且不再能恢复,协调者用下面的方法来解除阻塞:因为协调者是唯一一个决定提交或回滚的节点,(两阶段提交协议)所以可以使用超时机制来解决阻塞问题。如果协调者在一段时间之内没有收到来自cohort的消息,它将不再等待,直接向所有的cohort发送abort消息。这是这个协议的又一个缺点:它倾向于abort这样的case,而不是完成这个case


两阶段提交中的故障处理:超时和重发机制

首先说明(源自《数据库与事务处理》一书,为什么要这样设计?)
协调者对事务开始和提交消息进行强制写入到非易失性介质中;
参与者对准备消息和提交消息进行强制写入到非易失性介质中。。(两阶段提交协议)。
写入非易失性介质中的日志用于在节点崩溃之后能查找到崩溃时节点在事务中的状态。


由两阶段提交协议的工作原理可见,之所以能够在不丢失运行记录信息的情况下.从所有故障中迅速恢复,就是因为在执行过程中维护了事务日志,记录了执行恢复所需要的信息。

        现在来分析当发生不同类型故障时,2Pc的行为 。

n          站点故障
n          丢失报文
         网络分割

站点故障

(1)一参与者在把就绪记录写入运行记录以前出现故障。在这种情况下,协调者超时机制满期,它将采取撤消的决定。所有的参与者都撤销它们的子事务。当发生该故障的参与者恢复时,重启动过程简单地撤销该事务即可.不需要过问其他站点的情况
(2)一参与者在把就绪记录写入运行记录以后发生故障。在这种情况下,其他参与者的站点终止该事务(提交或撤消)。当故障站点恢复时,重启动过程不得不询问协调者或别的某个参与者关于该事务的结果(提交或撤消),然后执行相应的动作(提交或撤消)。这种情况下需要访问远程的恢复信息.
(3)协调者在把预备记录写入运行记录以后,而在写入global-commit或global-abort记录以前发生故障。这种情况下所有已经回答READY的参与者必须等待协调者恢复。协调者的重启动过程从头开始恢复提交协议,从预备记录(在运行记录中)读取参与者的标识,再次把PREPARE(预备)报发送给它们。每个就绪的参与者必须要识别出该新的PREPARE报文是前一个的重复报文。
(4)协调者在远行记录中写入global-commit或global-abort记录以后而在写入完成记录以前发生故障。这种情况下,协调者在重启动时必须再次给所有参与者发送其决定,未曾收到
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值