今天我们来看看2PC协议,不知道大家对2PC协议是不是了解,我们先简单介绍一下。

两阶段提交协议(two phase commit protocol, 2PC)可以保证数据的强一致性,许多分布式关系型数据管理系统采用此协议来完成分布式事务。它是协调所有分布式院子事务参与者,并决定提交或取消(回滚)的分布式算法。同时也是解决一致性问题的一致性算法。该算法能够解决很多的临时性系统故障(包括进程,网络节点,通信等故障),被广泛的使用。但是,它并不能通过配置来解决所有的问题,在某些情况下,它还需要人为的参与才能解决问题。参与者为了能够从故障中恢复,它们都使用日志来记录协议的状态,虽然使用日志降低了性能,但是节点能够从中恢复。

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

当事务的最后一步完成之后,协调器执行协议,参与者根据本地事务能够成功完成回复同意提交事务或者回滚事务。

顾名思义,两阶段提交协议由两个阶段组成,在正常情况下,两个阶段的执行过程如下所述:

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

在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协助者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。

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

在该阶段,协调者将基于第一个阶段的投票结果进行决策,提交或者取消。当且仅当所有的参与者同意提交事务协调器才通知所有的参与者提交事务,,否则协调者将通知所有参与者取消事务。参与者收到协调者发来的消息后将执行响应的操作。

协议操作流图如下:

两阶段提交最大的劣势是其通过阻塞完成的协议,在节点等待消息的时候处于阻塞状态,节点中其他进程则需要等待阻塞进程释放资源才能使用。如果协调者发生了故障,那么参与者将无法完成事务一直等待下去。以下情况可能导致节点发生永久阻塞:

如果参与者发送同意提交消息给协调者,进程将阻塞直至收到协调者的提交或者混滚的消息。如果协调器发生永久故障,参与者将一直等待,这里可以采用备份的协调器,所有参与者将恢复发给备份协调者,由它来承担协调器的功能。

如果协调器发送“请求提交”消息给参与者,它将被阻塞直到所有参与者回复了,如果某个参与者发生永久性故障,那么协调器也不会一直阻塞,因为协调器在某一时间内未收到参与者的消息,那么它通知其他参与者回滚事务。

同时两阶段提交协议没有容错机制,一个节点发生故障整个事务都要回滚,代价比较大。

下面看一个例子,来说明两阶段提交协议的工作流程:

A组织B、C和D三个人去爬长城:如果所有人都同意去爬长城,那么活动将举行;如果有一人不同意去爬长城,那么活动将取消。用2PC算法解决该问题的过程如下:

首先A将成为该活动的协调者,B、C和D将成为该活动的参与者。

阶段1:

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

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

阶段2:

此时A收到了所有活动参与者的邮件,并且A发现D下周三不能去爬山。那么A将发邮件通知B、C和D,下周三爬长城活动取消。

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

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

基于此,后来有人提出了三阶段提交协议,在其中引入超时的机制,将阶段1分解为两个阶段:在超时发生以前,系统处于不确定阶段;在超时发生以后,系统则转入确定阶段。

2PC协议包含协调者和参与者,并且二者都有发生问题的可能性。假如协调者发生问题,我们可以选出另一个协调者来提交事务。例如,班长组织活动,如果班长生病了,我们可以请副班长来组织。如果协调者出问题,那么事务将不会取消。例如,班级活动希望每个人都能去,假如有一位同学不能去了,那么直接取消活动即可。或者,如果大多数人去的话那么活动如期举行(2PC变种)。为了能够更好地解决实际的问题,2PC协议存在很多的变种,例如:树形2PC协议(或称递归2PC协议)、动态2阶段提交协议(D2PC)等。

好了,说了这么一大段协议的东西,我们接下来看看在PAT里面怎么实现这样的协议。

//Two Phase Commit Protocol (TPCP)

#define N 2; //number of pages
enum {Yes, No, Commit, Abort}; //constants
//channel result 0;
//channel inform 0;
channel vote ;
var hasNo = false; //The following models the coordinator
Coord(decC) = (|||{N}@ request -> Skip);
(|||{N}@ vote?vo -> atomic{tau{if (vo == No) {hasNo = true;}} -> Skip});
decide ->
(([hasNo == false] (|||{N}@inform.Commit -> Skip); CoordPhaseTwo(Commit)) [] ([hasNo == true] (|||{N}@inform.Abort -> Skip); CoordPhaseTwo(Abort)));
CoordPhaseTwo(decC) = |||{N}@acknowledge -> Skip; //The following models a page
Page(decP, stable) = request -> execute -> (vote!Yes -> PhaseTwo(decP) [] vote!No -> PhaseTwo(decP));
PhaseTwo(decP) = inform.Commit -> complete -> result.decP -> acknowledge -> Skip
[] inform.Abort -> undo -> result.decP -> acknowledge -> Skip;
//定义这个alphabet有什么用呢?
#alphabet Coord {request, inform.Commit, inform.Abort, acknowledge};
#alphabet Page {request, inform.Commit, inform.Abort, acknowledge}; System = Coord(Abort) || (|||{N}@Page(Abort, true));
Implementation = System \{request, execute, acknowledge, inform.Abort, inform.Commit, decide, result.Abort, result.Commit}; #assert System deadlockfree;
#define has hasNo == 1;
#assert System |= [](has -> <> undo);
#assert System |= [](request -> <> undo); Specification = PC(N);
PC(i) = [i == ](|||{N}@complete -> Skip)
[]
[i > ] (vote.Yes -> PC(i-) [] vote.No -> PU(i-));
PU(i) = [i == ](|||{N}@undo -> Skip)
[]
[i > ](vote.Yes -> PU(i-) [] vote.No -> PU(i-));
#assert Specification deadlockfree;
#assert Implementation refines Specification;

代码里面的参与者是pages,这里有两个人参与者。

枚举类型给了4个变量,Yes,No,Commit,Abort。值分别为0,1,2,3,Yes和No表示参与者返回给协调者的信息,如果都是YES(0),那么协调者就会确认(Commit,值为2),如果有一个参与者返回的是No(1),协调者就会让参与者回滚(Abort,值为3)。接下来定义一个通道(channel)vote,接下来定义一个变量(hasNo),表示这个参与者返回给协调者的信息里面是不是有No。

下面来对协调者进行建模:

Coord(decC) = (|||{N}@ request -> Skip);
(|||{N}@ vote?vo -> atomic{tau{if (vo == No) {hasNo = true;}} -> Skip});
decide ->
(([hasNo == false] (|||{N}@inform.Commit -> Skip); CoordPhaseTwo(Commit)) [] ([hasNo == true] (|||{N}@inform.Abort -> Skip); CoordPhaseTwo(Abort)));
CoordPhaseTwo(decC) = |||{N}@acknowledge -> Skip;

首先,协调者先向参与者发送请求,让参与者提交,然后协调者通过vote这个通道收到消息,然后如果收到的消息里面有No就把hasNo置为true。然后来做决定,然后是一个选择,每个选项前面有一个需要满足的条件([hasNo==false]或者[hasNo=true]),当hasNo为false时,说明每个参与者都返回给协调者Yes,那么协调者就会确认(Commit),然后开始第二阶段。第二阶段就是收到来自参与者的答复。

下面是对参与者进行建模:

Page(decP, stable) = request -> execute -> (vote!Yes -> PhaseTwo(decP) [] vote!No -> PhaseTwo(decP));
PhaseTwo(decP) = inform.Commit -> complete -> result.decP -> acknowledge -> Skip
[] inform.Abort -> undo -> result.decP -> acknowledge -> Skip;

参与者收到请求,然后开始执行然后进入选择,要么回复给协调者Yes,然后进入第二阶段,要么回复No,进入第二阶段。

第二阶段就是要么收到确认要么收到回滚,确认之后就是完成,然后进行相应的结果处理,然后给协调者一个答复。收到回滚后就开始回滚,然后对相应的结果进行处理,然后给协调者一个答复。

下面是定义了程序的变量集合:

#alphabet Coord {request, inform.Commit, inform.Abort, acknowledge};
#alphabet Page {request, inform.Commit, inform.Abort, acknowledge};

其实这一个我也不知道是什么意思。

First of all, alphabet of processes are calculated only when it is necessary, which means, only when a parallel composition is evaluated. 
This saves a lot of computational overhead. Processes in a large number of models only communicate through shared variables.
If no parallel composition is present, there is no need to evaluate alphabets.
We remark that when there is no shared abstract events, process P ||| Q and P || Q are exactly the same. Therefore, we recommend ||| when appropriate.
When a parallel composition is evaluated for the first time, the default alphabet of each sub-process is calculated (if not manually defined).
The procedure for calculating the default alphabet works by retrieving all event constituting the process expression and unfolding every newly-met process reference.
It throws an exception if a process reference is met twice with different parameters (which probably indicates the unfolding is non-terminating).
In such a case, PAT expects the user to specify the alphabet of a process using the following syntax, #alphabet P {...}; where P is process name and {...} is the set of events which is considered its alphabet. Notice that the event expressions may contain variables.

翻译过来就是:

首先,仅当必要时才计算出进程的字母表,这意味着只有当并行组合被评估时。
这节省了大量的计算开销。大量模型中的进程只能通过共享变量进行通信。
如果不存在平行组合,则不需要评估字母。我们说当没有共享抽象事件时,进程P ||| Q和P || Q完全一样。
因此,我们推荐|||适当时候当首次评估并行组合时,会计算每个子进程的默认字母表(如果没有手动定义)。
计算默认字母表的过程通过检索构成过程表达式的所有事件并展开每个新满足的过程引用。
如果一个进程引用被两次不同的参数(这可能表示展开是不终止的),它会抛出异常。
在这种情况下,PAT希望用户使用以下语法来指定进程的字母表, #alphabet P {...}; 其中P是进程名称,{...}是被认为是其字母表的事件集合。请注意,事件表达式可能包含变量。

大概就是说这个字母表是可以自己指定,如果自己不指定那么PAT就会自动生成一个字母表。指定字母表有一个好处就是当程序比较复杂时,PAT就只是照着指定的字母表运行。

然后接下来就是整个系统,整个系统就是协调者和参与者的并发。

接下来Implementation表示在System的基础上隐藏一些行为。

下面就是一些验证:

首先验证System会不会不死锁。

然后定义了has 变量为 hasNo==1;意思就是hasNo为true,也就是说参与者有回复No的,由此可以看出

#assert System |= [](has -> <> undo);

是正确的,因为有人回复No,那么就会回滚。

下面这个验证是不正确的,因为协调者让参与者commit,但是不一定会回滚。

#assert System |= [](request -> <> undo);

接下来是一个行为,

Specification = PC(N);
PC(i) = [i == ](|||{N}@complete -> Skip)
[]
[i > ] (vote.Yes -> PC(i-) [] vote.No -> PU(i-));
PU(i) = [i == ](|||{N}@undo -> Skip)
[]
[i > ](vote.Yes -> PU(i-) [] vote.No -> PU(i-));

这个行为大家看看是什么意思,有点复杂,我也不想解释了。

接下来是两个验证:

#assert Specification deadlockfree;
#assert Implementation refines Specification;

这两个验证都是正确的。至于为什么正确我也不想解释了,因为这一块我不是太懂。

形式化验证工具(PAT)2PC协议学习的更多相关文章

  1. 形式化验证工具(PAT)Reader-Writers Problem学习

    经过前几次的学习,我们应该对PAT有一点点的了解了,我们加下来就直接看例子中的一个问题,这个问题比较简单. 看代码: //The classic Readers/Writers Example mod ...

  2. 形式化验证工具(PAT)羊车门代码学习

    首先介绍一下PAT工具,下图是PAT工具的图标 PAT工具全称是Process Analysis Toolkit,可以做一些简单的验证. 今天我们分析一下例子里面的Monty Hall Problem ...

  3. 形式化验证工具(PAT)Perterson Algorithm学习

    今天学习一下Perterson Algorithm. 这个算法是使用三个变量来实现并发程序的互斥性算法. 具体看一下代码: Peterson算法是一个实现互斥锁的并发程序设计算法,核心就是三个标志位是 ...

  4. RChain的一键形式化验证:关于RCast 33 – LADL话题的讨论摘要

    作者/Atticbee 在这一集,Greg和RChain的研究人员Isaac,Christian讨论了TLA(Temporal Logic of Actions)和RChain的LADL(Logic ...

  5. http协议学习系列

    深入理解HTTP协议(转)  http://www.blogjava.net/zjusuyong/articles/304788.html http协议学习系列   1. 基础概念篇 1.1 介绍 H ...

  6. http协议学习系列(一个博文链接)

    深入理解HTTP协议(转) http协议学习系列(转自:http://www.blogjava.net/zjusuyong/articles/304788.html) 1. 基础概念篇 1.1 介绍 ...

  7. 转 #HTTP协议学习# (一)request 和response 解析

    http://www.cnblogs.com/bukudekong/p/3834020.html #HTTP协议学习# (一)request 和response 解析   注:本文转自:http:// ...

  8. 《形式化分析工具Scyther性能研究》------摘抄整理

    本篇论文的主要创新点在--------使用 Scyther工具发现对部分 KCI攻击搜索出现漏报的现象,并给出了存在的原因, 介绍了 形式化分析工具   AVispa全称是   Automated V ...

  9. Scyther 形式化分析工具资料整理(三)

    1.作者Cas Cremers在做TLS1.3的时候我么发现并没有使用Scyther 形式化丰分析工具对其进行分析,而是使用了 The Tamarin .作者建立了TLS.13的模型. 那么我的目标是 ...

随机推荐

  1. uva1605 - Building for UN(构造法)

    这道题构造出的结果很妙,考察思维能力.就两层,每层都n*n个格子,第一层第i行都放国家i,第二层第j列都放国家j. 需要注意的是ASCII中A至Z在a至z的前面(数字小),而且它们两组不挨着.所以需要 ...

  2. BRICH

    一.简介 Brich是典型的基于层次的聚类算法.最大的特点就是适合数据量特别大的数据集,处理速度很快,因为该算法扫描一遍数据集. 该算法是利用了一个树状结构来快速聚类,该结构类似平衡B+树.每一个叶子 ...

  3. Linux运维工程师中级面试题

    1.解释top命令和vmstat命令 2.请写出iptables语句 3.mysql高可用方案有哪些?mysql备份方案有哪些?有什么缺点? 4.写出Apache 2.x的两种工作模式,以及各自的工作 ...

  4. PHP JSON文件解析并获取key、value,判断key是否存在

    /****************************************************************************** * PHP JSON文件解析并获取key ...

  5. 【机器学习】集成学习之xgboost的sklearn版XGBClassifier使用教程

    XGBClassifier是xgboost的sklearn版本.代码完整的展示了使用xgboost建立模型的过程,并比较xgboost和randomForest的性能. # -*- coding: u ...

  6. LeetCode 480. Sliding Window Median

    原题链接在这里:https://leetcode.com/problems/sliding-window-median/?tab=Description 题目: Median is the middl ...

  7. bzoj 4059:Non-boring sequences 分治

    题目: 我们害怕把这道题题面搞得太无聊了,所以我们决定让这题超短.一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字,即每个子序列里至少存在一个数字只出现一次.给定一个整数序列,请 ...

  8. hashmap&linkedlist

    import java.util.HashMap; import java.util.Iterator; public class hashmap1 { public static void main ...

  9. UDP打洞原理及代码

    来源:http://www.fenbi360.net/Content.aspx?id=1021&t=jc UDP"打洞"原理 1.       NAT分类 根据Stun协议 ...

  10. 从XML文件乱码问题,探寻其背后的原理 (ZHUAN)

    出现应用程序读取XML文件乱码的场景: 加入xml文件以<?xml version="1.0" encoding="utf-8" ?> 格式的:如果 ...