分布式计算 ——原理、算法与系统(Distributed Computing —— Principles, Algorithms, and System)读书笔记 持续更新

分布式计算 ——原理、算法与系统

Distributed Computing —— Principles, Algorithms, and System

不定期更新
中文书翻译很烂。。。各种下标错误等。。

本读书笔记首发于:https://github.com/rsy56640/Distributed_System_Learning/tree/master/Distributed Computing Principles%2C Algorithms%2C and Systems - Kshemkalyani
 

 

第一章 引言

分布式系统:处理器、存储器、通信网络

1.4 与并行多处理器/多计算机系统的关系

并行系统:通过将计算任务在多个处理器之间进行分配,从而获得更高的吞吐率
  • 多处理器系统
    • 互联网络:Omega网络、蝴蝶网络

在这里插入图片描述

图左是UMA(均匀存储器访问体系结构),右NUMA

  • 多计算机并行系统

    • 处理器无法直接访问共享内存
    • 互联网络:环,超立方体
  • 阵列处理器

基于指令流和数据流的分类

在这里插入图片描述

1.5 消息传递系统与共享内存系统的对比

  • 通过消息传递进行通信
  • 通过共享内存通信
在消息传递系统上仿真共享内存

每一个共享的位置可以建模为一个隔离的进程。

处理器间通过共享内存进行通信;计算机间使用消息传递进行通信。

1.6 分布式通信的原语

消息发送原语Send(), 消息接受原语Receive()

Send()发送方式:缓冲(拷贝到内核缓冲区,再到网络)与 非缓冲(直接拷贝到网络)。

Receive()通常采用缓冲方式。

  • 同步原语:如果Send()Receive()两端都实现了握手,则原语是同步的
    • 只有调用者知道对应的Receive()原语被调用并且接受操作完成,Send()原语才算完成
    • 当数据拷贝到接收方的用户缓冲区时,Receive()原语被认为完成
  • 异步原语:
    • 如果需要发送的数据被拷贝出用户缓冲区后,控制流程返回到调用进程,Send()原语被称为异步的
    • 异步的Receive()原语无意义
  • 阻塞原语:如果一个原语的处理完成之后控制流程返回到调用进程,则一个原语被称为阻塞的
  • 非阻塞原语:如果一个控制流程在调用原语之后立刻返回到调用进程,甚至这个操作尚未完成,则这个原语被称为非阻塞的

怎样理解阻塞非阻塞与同步异步的区别?- 知乎

处理器同步性

同步屏障:保证在所有处理器完成前面所分配的指令之前都不会去执行下一步的代码。

1.7 同步与异步执行

异步执行
  • 没有处理器的同步
  • 消息延迟(传输+传播时间)是有限的
  • 对于一个进程执行某一步任务没有时间上的上界限制
同步执行
  • 处理器之间同步
  • 消息分发(传输+分发时间)能够在一个逻辑步或者轮次内完成
  • 进程执行一步具有一个已知的上界

同步系统实际上是一个特殊的异步系统——所有的通信都在其发起的轮次内完成。

1.7.3 仿真

在无错误的系统中,异步/同步 共享内存/消息传递 这4类程序可以互相仿真,即等价。

在这里插入图片描述

但是在有错误的系统中,情况并不是这样;一个同步系统相比于异步系统具有更高的可计算性。

1.8 设计主题与挑战

1.8.1 系统角度
  • 通信:远程过程调用、远程对象调用、面向消息的通信、面向流的通信
  • 进程
  • 命名:对于资源和进程的标识以及定位
  • 同步:leader, logic clock
  • 数据存储与访问
  • 一致性与复本
  • 容错
1.8.2 算法角度

 

第二章 分布式计算模型

在分布式系统中,通信消息可能在传递过程中乱序丢失受到篡改或者重复传递

分布式系统可以以一个有向图的方式建模,其中结点表示处理器而边则表示单向通信信道。

2.1 分布式程序

分布式程序有一组 n n n 个异步进程 p 1 ,   p 2 ,   . . . ,   p n p_1,\ p_2,\ ...,\ p_n p1, p2, ..., pn 组成,令 C i j C_{ij} Cij 表示从进程 p i p_i pi 到进程 p j p_j pj 的通信信道, m i j m_{ij} mij 表示由 p i p_i pi 发往 p j p_j pj 的消息。

  • 假设每个进程都各自运行在不同的处理器上
  • 进程间没有可共享的全局存储,而只能通过消息传递来进行联系
  • 通信延迟是有限但无法预测的
  • 这些进程不共享一个可随时访问的全局时钟
  • 进程运行和消息传送是异步的

2.2 分布式运行模型

一个进程的运行可以描述为三类原子操作:内部事件消息发送事件消息接受事件。令 e i x e_i^x eix 表示进程 p i p_i pi 上的第 x x x 个事件。对一个消息 m m m,令 s e n d ( m ) send(m) send(m) r e c ( m ) ​ rec(m)​ rec(m) 分别表示其发送和接收的消息。一个内部事件改变其所处的进程的状态,一个发送或接受事件改变双方的状态。

进程中的事件以出现顺序进行排序: e i 1 ,   e i 2 ,   . . . ,   e i x ,   . . . e_i^1,\ e_i^2,\ ...,\ e_i^x,\ ... ei1, ei2, ..., eix, ...,该序列记为 H i \mathcal{H}_i Hi
H i = ( h i , → i ) \mathcal{H}_i = (h_i, \to_i) Hi=(hi,i)
其中 h i h_i hi 是由 p i p_i pi 产生的事件集合;二元关系 → i \to_i i 则定义了这些事件间的序。

关系 → m s g \to_{msg} msg 表示因消息交换所导致的因果依赖关系:
s e n d ( m ) → m s g r e c ( m ) send(m) \to_{msg} rec(m) send(m)msgrec(m)

1. 因果优先关系

H = ⋃ h i H = \bigcup h_i H=hi 表示在一次分布式计算过程中执行的时间集合。我们在集合 H H H 上定义一个关系 → \to ,表示事件间的因果依赖关系。
KaTeX parse error: No such environment: equation at position 93: …htarrow \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \left\{ …
关系 → \to 是 Lamport 的 “happerns before” 关系。如果 e i → e j e_i \to e_j eiej,则事件 e j e_j ej 直接或间接依赖于 e i e_i ei,存在着一条起始于 e i e_i ei,终止于 e j e_j ej 的路径。

e i ↛ e j e_i \nrightarrow e_j eiej 表示事件 e j e_j ej 不直接或间接依赖于 e i e_i ei即事件 e i e_i ei 不会对 e j ​ e_j​ ej 产生因果影响

并发:如果 e i ↛ e j e_i \nrightarrow e_j eiej e j ↛ e i e_j \nrightarrow e_i ejei,则事件 e i e_i ei e j e_j ej 被称为是并发的,其关系被记为 e i ∣ ∣ e j e_i || e_j eiej

2. 逻辑并发和物理并发

两个事件是逻辑并发的,当且仅当它们之间无因果影响。与此相对,物理并发的含义是不同事件在物理时间的同一时刻发生。不论一组逻辑并发的事件是否在物理时间上同时发生,也无论它们在物理时间上实际发生的顺序如何,都不会改变计算的结果。因此。虽然一组逻辑并发的事件可能不会在物理时间的同一时刻发生,但我们总是假定这些事件在物理时间的同一时刻发生

2.3 通信网络模型

因果依赖(CO):对任意两个消息 m i j m_{ij} mij m k j m_{kj} mkj,假设 s e n d ( m i j ) → s e n d ( m k j ) send(m_{ij}) \to send(m_{kj}) send(mij)send(mkj),则 r e c ( m i j ) → r e c ( m k j ) rec(m_{ij}) \to rec(m_{kj}) rec(mij)rec(mkj)。确保那些发往同一目标的因果依赖的消息,以符合它们之间因果依赖关系的顺序进行发送。

通信网络模型:CO ⊂ \subset FIFO ⊂ \subset 非FIFO(随机)

因果依赖模型提供了一个内在的同步机制。

2.4 分布式系统的全局状态

分布式系统的全局状态是所有 处理器状态信道状态 的集合。

处理器状态:令 L S i x LS_i^x LSix 表示处理器 p i p_i pi 在事件 e i x e_i^x eix 发生之后,以及事件 e i x + 1 e_i^{x+1} eix+1 发生之前的状态。特别地, L S i 0 LS_i^0 LSi0 表示 p i p_i pi 的初始状态。

y ≤ x y \le x yx,记为 e i y ≤ L S i x e_i^y \le LS_i^x eiyLSix,表示 e i y e_i^y eiy 在处理器状态之内。

信道状态:记 S C i j x , y ​ SC_{ij}^{x, y}​ SCijx,y 表示信道 C i j ​ C_{ij}​ Cij 的状态:
S C i j x , y = { m i j   ∣   s e n d ( m i j ) ≤ L S i x   ∧   r e c ( m i j ) ≰ L S j y } SC_{ij}^{x, y} = \{ m_{ij}\ |\ send(m_{ij}) \le LS_i^x\ \land\ rec(m_{ij}) \nleq LS_j^y \} SCijx,y={mij  send(mij)LSix  rec(mij)LSjy}
通俗地讲,信道是消息的集合。消息 m i j m_{ij} mij p i p_i pi x x x 之内 send; p j p_j pj y y y 之后 rec。即 S C i j x , y = { m i j   ∣   m i j   从 左 穿 过 线 段 e i x + 1 e j y } SC_{ij}^{x, y} = \{ m_{ij}\ |\ m_{ij}\ 从左穿过线段e_i^{x+1} e_j^y \} SCijx,y={mij  mij 穿线eix+1ejy}

在这里插入图片描述

全局状态:$GS = {\ \cup_iLS_i^{x_i},\ \cup_{j, k}SC_{jk}^{y_j, z_k} } $

一致性全局状态:称 G S GS GS 是一致的,如果满足
∀ m i j :   s e n d ( m i j ) ≰ L S i x i ⇒ m i j ∉ S C i j x i , y j   ∧   r e c ( m i j ) ≰ L S j y j \forall m_{ij}:\ send(m_{ij}) \nleq LS_i^{x_i} \Rightarrow m_{ij} \notin SC_{ij}^{x_i, y_j}\ \land\ rec(m_{ij}) \nleq LS_j^{y_j} mij: send(mij)LSiximij/SCijxi,yj  rec(mij)LSjyj

这里中文书上写错了,第一个符号写成 ≤ \le

通俗地讲: p i p_i pi x i x_i xi 之后的 send,那么必须有 p j p_j pj y j ​ y_j​ yj 之后 rec。即
∀ m i j , m i j   不 能 从 右 穿 过 线 段 e i x e j y + 1 \forall m_{ij}, m_{ij}\ 不能从右穿过线段e_i^x e_j^{y+1} mij,mij 穿线eixejy+1

在这里插入图片描述

非中转全局状态:所有信道为空

强一致的全局状态:一致且非中转的

2.5 分布式计算的运行分割

一条分割线 C C C 将时空图分割为2各部分, P A S T ( C ) PAST(C) PAST(C) 表示分割线左边的所有事件的集合, F U T U R E ( C ) FUTURE(C) FUTURE(C) 表示分割线右边的所有事件的集合。

在这里插入图片描述

C 1 C_1 C1 是非一致性分割线; C 2 C_2 C2 是一致性分割线

一致性全局状态对应一条分割线,其中每个 P A S T PAST PAST 集合中的 rec 事件都是在 P A S T PAST PAST 集中 send。

一条分割线是非一致性的,如果某个消息跨越了分割线从 F U T U R E FUTURE FUTURE 集传到 P A S T PAST PAST 集。

2.6 事件的过去和未来锥面

P a s t ( e j ) Past(e_j) Past(ej) 表示在计算 ( H , → ) (H, \to) (H,) e j e_j ej 的过去事件。则,
P a s t ( e j ) = { e i   ∣   ∀ e i ∈ H , e i → e j } Past(e_j) = \{ e_i\ |\ \forall e_i \in H, e_i \to e_j \} Past(ej)={ei  eiH,eiej}
P a s t i ( e j ) Past_i(e_j) Pasti(ej) 是进程 p i p_i pi 上所有属于 P a s t ( e j ) Past(e_j) Past(ej) 事件的集合。注意到 P a s t i ( e j ) Past_i(e_j) Pasti(ej) 是全序的,最大元素记为 m a x ( P a s t i ( e j ) ) max(Past_i(e_j)) max(Pasti(ej))。注意到 m a x ( P a s t i ( e j ) ) max(Past_i(e_j)) max(Pasti(ej)) 总是一个 send 事件。

M a x _ P a s t ( e j ) = ⋃ i { m a x ( P a s t i ( e j ) ) } Max\_Past(e_j) = \bigcup_i \{ max(Past_i(e_j)) \} Max_Past(ej)=i{max(Pasti(ej))},其包含每个进程上影响 e j e_j ej 的最新事件,它被称为事件 e j e_j ej 的过去锥面。

用反证法易知: M a x _ P a s t ( e j ) Max\_Past(e_j) Max_Past(ej) 是一条一致性分割线。

在这里插入图片描述

类似地,事件 e j e_j ej 的未来事件记作 F u t u r e ( e j ) Future(e_j) Future(ej),它包含所有受到 e j e_j ej 影响的事件 e i e_i ei,定义为
F u t u r e ( e j ) = { e i   ∣   ∀ e i ∈ H , e j → e i } Future(e_j) = \{ e_i\ |\ \forall e_i \in H, e_j \to e_i \} Future(ej)={ei  eiH,ejei}
定义 F u t u r e i ( e j ) Future_i(e_j) Futurei(ej) 为进程 p i p_i pi 上所有属于 F u t u r e ( e j ) Future(e_j) Future(ej) 事件的集合。 m i n ( F u t u r e i ( e j ) ) min(Future_i(e_j)) min(Futurei(ej)) 作为 p i p_i pi 上受 e j e_j ej 影响的第一个事件。注意到 m i n ( F u t u r e i ( e j ) ) min(Future_i(e_j)) min(Futurei(ej)) 总是 rec 事件。 M i n _ F u t u r e ( e j ) Min\_Future(e_j) Min_Future(ej) 定义为 ⋃ i { m i n ( F u t u r e i ( e j ) ) } \bigcup_i \{ min(Future_i(e_j)) \} i{min(Futurei(ej))},它包含了每个进程上受到事件 e j e_j ej 影响的第一个事件的集合,被称为事件 e j e_j ej 的未来锥面。易证它是一条一致性分割线。

在一个计算 H H H 中,一个事件 e e e e j ​ e_j​ ej并发的,当且仅当
e ∈ H − P a s t ( e j ) − F u t u r e ( e j ) e \in H - Past(e_j) - Future(e_j) eHPast(ej)Future(ej)

2.8 本章小结

进程间的消息交换显示出进程间的信息流向并且建立了进程间的因果依赖关系。进程间的优先因果关系由 Lamport 的 hanppens-before 关系确定。

 

第三章 逻辑时间

逻辑时间系统由一个时间域 T T T 和一个逻辑时钟 C C C 组成。 T T T 的元素形成关系 &lt; &lt; < (happens-before) 上的偏序集合。逻辑时钟 C C C 是一个函数,把事件 e e e 映射到时间域 T T T 中的一个元素,表示为 C ( e ) C(e) C(e) 且成为 e e e 的时间戳,定义为
C : H ↦ T C:H\mapsto T C:HT
使得对于事件 e i ​ e_i​ ei e j ​ e_j​ ej,有 e i → e j ⇒ C ( e i ) &lt; C ( e j ) ​ e_i \to e_j \Rightarrow C(e_i) &lt; C(e_j)​ eiejC(ei)<C(ej) ,这种单调性称为时钟一致性条件。

e i → e j ⇔ C ( e i ) &lt; C ( e j ) e_i \to e_j \Leftrightarrow C(e_i) &lt; C(e_j) eiejC(ei)<C(ej),则这个系统称为强一致的

每个进程 p i p_i pi 维护数据结构:

  • 本地逻辑时钟: l c i lc_i lci,表示进程 p i p_i pi 的进度
  • 全局时钟: g c i gc_i gci,表示进程 p i p_i pi 从本地视角所见的全局逻辑时间

更新数据结构的协议:

  • R1 规则:管理当进程执行一个事件时,如何更新本地逻辑时钟
  • R2 规则:管理进程如何更新全局逻辑时钟,使得全局进展和进程所见的全局时间得以更新

3.3 标量时间

3.3.1 定义

时间域 T = N T = N T=N,进程 p i p_i pi 的本地逻辑时钟和本地全局时钟用同一个整数 C i C_i Ci 表示。

(1) R1 规则:

在执行一个事件之前,进程 p i p_i pi 执行如下动作:
C i : = C i + d , d &gt; 0 C_i := C_i + d, \quad d &gt; 0 Ci:=Ci+d,d>0
通常 d d d 会有不同的值,典型的值保持为 1.

(2) R2 规则:

每个消息附加有它的发送方在发送时的时钟值,当进程 p i p_i pi 接收到一个带有时间戳 C m s g C_{msg} Cmsg 的消息时,它执行如下动作:

  1. C i : = m a x ( C i , C m s g ) ; C_i := max(C_i, C_{msg}); Ci:=max(Ci,Cmsg);
  2. 执行 R1 规则
  3. 传递该消息

在这里插入图片描述

3.3.2 基本性质
1. 一致性(consistency)

单调性蕴含了一致性。

2. 全序(total ordering)

注意到 C ( e i ) = C ( e j ) ⇒ e i ∣ ∣ e j C(e_i) = C(e_j) \Rightarrow e_i || e_j C(ei)=C(ej)eiej

定义时间戳为 ( t , i ) (t, i) (t,i),其中 t t t 是本地逻辑时钟, i i i 是进程号。

全序关系 ≺ \prec 定义如下:
KaTeX parse error: Expected 'EOF', got '\or' at position 36: …arrow (h < k)\ \̲o̲r̲\ ((h = k)\ \la…
注意到 x ≺ y ⇒ x → y   ∨   x ∣ ∣ y x\prec y \Rightarrow x\to y \ \lor \ x||y xyxy  xy,这没啥用。

3. 事件计数

如果 d d d 总是1,则标量时间有如下性质:对于一个事件 e e e 和时间戳 h h h,在这之前一定顺序产生了 h − 1 h-1 h1 个事件,不管是哪些进程产生的。

4. 非强一致性

C ( e i ) &lt; C ( e j ) ⇏ e i → e j C(e_i) &lt; C(e_j) \nRightarrow e_i \to e_j C(ei)<C(ej)eiej

本地逻辑时钟和本地全局时钟被压缩成一个,导致了不同进程的事件之间因果依赖关系的缺失。

3.4 向量时间

3.4.1 定义

时间域 T = N n T = N^n T=Nn,每个进程维护一个向量 v t i [ 1.. n ] vt_i[1..n] vti[1..n],其中 v t i [ j ] vt_i[j] vti[j] 表示进程 p i p_i pi 的有关 p j p_j pj 本地时间的最近信息。用整个向量 v t i vt_i vti 代表 p i p_i pi 所见的全局时间,并且用于给事件打上时间戳。

(1) R1 规则:

在执行一个事件之前,进程 p i p_i pi 更新其本地逻辑时钟如下:
v t i [ i ] : = v t i [ i ] + d vt_i[i] := vt_i[i] + d vti[i]:=vti[i]+d
(2) R2 规则:

把每个消息 m m m 加入到发送方进程在发送时的向量时钟 v t vt vt。一旦接收到这样一个消息 ( m , v t ) (m, vt) (m,vt),进程 p i p_i pi 执行如下一系列动作:

  1. 更新它的全局逻辑时间如下:

v t i [ k ] : = m a x ( v t i [ k ] , v t [ k ] ) , k ∈ [ 1 , n ] vt_i[k] := max(vt_i[k], vt[k]),\quad k\in[1, n] vti[k]:=max(vti[k],vt[k]),k[1,n]

  1. 执行 R1 规则
  2. 传送消息 m

在这里插入图片描述

3.4.2 基本性质
1. 同构

( H , → ) (H, \to) (H,) 同构于 ( T , &lt; ) ​ (T, &lt;)​ (T,<)
e i x → e j y ⇔ v x &lt; v y e i x   ∣ ∣   e j y ⇔ v x   ∣ ∣   v y e i x → e j y ⇔ v x [ i ] ≤ v y [ i ] e i x   ∣ ∣   e j y ⇔ ( v x [ i ] &gt; v y [ i ] )   ∧   ( v x [ j ] &lt; v y [ j ] ) \begin{aligned} e_i^x \to e_j^y &amp; \Leftrightarrow vx &lt; vy \\ e_i^x\ ||\ e_j^y &amp; \Leftrightarrow vx \ ||\ vy \\ \\ e_i^x \to e_j^y &amp; \Leftrightarrow vx[i]\le vy[i] \\ e_i^x\ ||\ e_j^y &amp; \Leftrightarrow (vx[i] &gt; vy[i]) \ \land\ (vx[j] &lt; vy[j]) \\ \end{aligned} eixejyeix  ejyeixejyeix  ejyvx<vyvx  vyvx[i]vy[i](vx[i]>vy[i])  (vx[j]<vy[j])

2. 强一致性
3. 事件计数

3.5 向量时钟的有效实现

时间戳长度过大,需要优化

3.5.1 Singhal - Kshemkalyani 的差量技术

p i p_i pi p j p_j pj 发送消息时,将现在的向量与上一次发送给 p j p_j pj 的向量做差,为 0 0 0 的忽略,其余的标记位置当前值打包发送。

在这里插入图片描述

p i p_i pi p j p_j pj 发送 { ( i , v ) } \{(i,v)\} {(i,v)} p j p_j pj 更新向量如下:
v t j ( i k ) = m a x ( v t i [ k ] , v k ) vt_j(i_k) = max(vt_i[k], v_k) vtj(ik)=max(vti[k],vk)
每个进程需要记录上一次发给其他所有进程的时间戳向量。这个技术的主要价值在于将进程的存储空间开销降低到 O ( n ) O(n) O(n),方式如下:

进程 p i p_i pi 维护两个向量:

  • LastSent: L S i [ 1.. n ] LS_i[1..n] LSi[1..n] L S i [ j ] LS_i[j] LSi[j] 表示当 p i p_i pi 上一次发送消息给 p j p_j pj v t i [ i ] vt_i[i] vti[i] 的值
  • LastUpdate: L U i [ 1.. n ] LU_i[1..n] LUi[1..n] L U i [ j ] LU_i[j] LUi[j] 表示当 p i p_i pi 上一次更新 v t i [ j ] vt_i[j] vti[j] 项时 v t i [ i ] vt_i[i] vti[i] 的值

显然有 L U i [ i ] = v t i [ i ] LU_i[i] = vt_i[i] LUi[i]=vti[i],并且只有 rec 导致更新 v t i [ j ] vt_i[j] vti[j] L U i [ j ] LU_i[j] LUi[j] 才需要更新;只有 p i p_i pi send p j pj pj L S i [ j ] LS_i[j] LSi[j] 需要更新。

因此,从上次 p i p_i pi p j p_j pj 通信以来,向量时钟中只有 v t i [ k ] vt_i[k] vti[k] 改变,其中 k k k 满足 L S i [ j ] &lt; L U i [ k ] LS_i[j] &lt; LU_i[k] LSi[j]<LUi[k]。于是 p i p_i pi 发送 p j p_j pj 的集合为:
{   ( x , v t i [ x ] )   ∣   L S i [ j ] &lt; L U i [ x ]   } \{\ (x, vt_i[x])\ |\ LS_i[j] &lt; LU_i[x]\ \} { (x,vti[x])  LSi[j]<LUi[x] }
通俗地讲:记录上次通信以来本地时钟的改变,因为 r e c rec rec 操作总是会更新本地时钟。

3.5.2 Fowler - Zwaenepoel 的直接依赖技术

中文版怎么回事???各种下标错误,我才看到P51,都有好几处错误了。还有不少翻译很迷。。

通俗地讲:只计算直接依赖,也就是通过消息同步的事件,或者本地事件。

每个进程维护一个依赖向量 D i [ 1.. n ] = { 0 } D_i[1..n] = \{0\} Di[1..n]={0},按如下方式更新:

  1. p i p_i pi 发生事件时, D i [ i ] : = D i [ i ] + 1 D_i[i] := D_i[i] + 1 Di[i]:=Di[i]+1
  2. p i p_i pi p j p_j pj 发送消息时,加入更新过的 D i [ i ] D_i[i] Di[i]
  3. p i p_i pi p j p_j pj 接收消息时,更新 D i [ j ] : = m a x ( D i [ j ] , d ) D_i[j] := max(D_i[j], d) Di[j]:=max(Di[j],d)

在这里插入图片描述

依赖向量 D i D_i Di 仅仅反应直接依赖。(图中 p 2 p_2 p2 直接依赖于 p 3 p_3 p3 p 3 p_3 p3 直接依赖于 p 4 p_4 p4,但 p 2 p_2 p2 不知道自己间接依赖于 p 4 p_4 p4

方法:间接依赖可以通过脱机的递归跟踪事件的直接依赖向量来获得。

这个方法适用于不要求频繁计算传递依赖的应用,如因果断点和异步检查点的恢复等离线计算。

离线计算算法:递归地更新过去锥面。

缺点:如果事件频繁的发生,该技术需要记录大量事件的历史。

3.6 Jard - Jourdan 的自适应技术

3.7 矩阵时间

3.7.1 定义

T = N n × n T = N_{n \times n} T=Nn×n,进程 p i p_i pi 维护一个矩阵 m t i [ 1.. n ] [ 1.. n ] mt_i[1..n][1..n] mti[1..n][1..n]

m t i [ i ] [ i ] mt_i[i][i] mti[i][i] 表示 p i p_i pi 本地逻辑时钟;

m t i [ i ] [ j ] mt_i[i][j] mti[i][j] 表示 p i p_i pi 具有的有关进程 p j p_j pj 的本地逻辑时钟的最新知识;

m t i [ j ] [ k ] mt_i[j][k] mti[j][k] 表示 p i p_i pi 具有的有关进程 p j p_j pj 的知识,该知识是 p j p_j pj 具有的 p k p_k pk 本地逻辑时钟的最新知识;

通俗地讲:就是存储了所有 v t i [ 1.. n ] vt_i[1..n] vti[1..n]

(1) R1 规则:

在执行一个事件前,进程 p i p_i pi 更新本地逻辑时钟:
m t i [ i ] [ i ] : = m t i [ i ] [ i ] + d mt_i[i][i] := mt_i[i][i] + d mti[i][i]:=mti[i][i]+d
(2) R2 规则:

每个消息附带矩阵时间 m t mt mt,当 p i p_i pi 收到 p j p_j pj 的消息 ( m , m t ) (m,mt) (m,mt) 时, p i p_i pi 执行如下:

  1. 更新矩阵如下:

m t i [ i ] [ k ] : = m a x ( m t i [ i ] [ k ] , m t [ j ] [ k ] ) , k ∈ [ 1 , n ] m t i [ k ] [ l ] : = m a x ( m t i [ k ] [ l ] , m t [ k ] [ l ] ) , k , l ∈ [ 1 , n ] \begin{aligned} mt_i[i][k] &amp; := max(mt_i[i][k], mt[j][k]),\quad k\in [1,n] \\ mt_i[k][l] &amp; := max(mt_i[k][l], mt[k][l]),\quad k,l\in [1,n] \end{aligned} mti[i][k]mti[k][l]:=max(mti[i][k],mt[j][k]),k[1,n]:=max(mti[k][l],mt[k][l]),k,l[1,n]

  1. 执行 R1 规则
  2. 发送消息 m

在这里插入图片描述

通俗地讲:总是保证 m t i [ i ] [ . ] ≥ m t i [ j ] [ . ] , j ≠ i mt_i[i][.] \ge mt_i[j][.],\quad j \neq i mti[i][.]mti[j][.],j̸=i;即总是保证自己关于其他进程的知识在自己的矩阵时间里是最新的。

3.7.2 基本性质

m t i [ i ] [ . ] mt_i[i][.] mti[i][.] 包含了向量时钟的所有性质。此外还具有如下性质:

$min_k(mt_i[k][l] \ge t) \Rightarrow\ $进程 p i p_i pi 知道 “每个进程知道 p l p_l pl 本地时间的进展,直到 t t t”,这意味着进程 p i p_i pi 知道 “所有其他进程知道 p l p_l pl 不会发送本地时间 ≤ t \le t t 的消息”,据此可以做一些优化。

3.8 虚拟时间

3.8.1 虚拟时间的定义

虚拟时间是分布式计算中一个全局的、一维的临时坐标系统,以测量计算的进展和定义同步。

消息:发送方,虚拟发送时间,接收方,虚拟接收时间。

  • 规则 1:每个消息的虚拟发送时间 &lt; &lt; < 该消息的虚拟接收时间

  • 规则 2:进程中事件的虚拟时间是递增的

感觉像是标量时间到向量时间的一种过渡,但是表达力不及向量时间。

3.8.2 与 Lamport 逻辑时钟的比较

在虚拟时间中,时钟是按照乐观方式前进的,一旦检测到违反的情况就会采取纠正行动。

3.8.3 时间变形机制

消息的虚拟接收时间被认为是其时间戳

时间变形机制的组成:

  • 本地控制机制:保证按正确的次序执行事件和处理消息
  • 全局控制机制:处理全局进展、终止检测、I/O错误处理、流控制等
3.8.4 本地控制机制

进程只有本地时钟,没有全局时钟。

一个消息的虚拟发送时间为发送方的时钟。

虚拟时间的语义要求输入消息严格按时间戳次序被每个进程接收。实现该要求的唯一方法是:在收到一个迟到的消息时,接收者回滚到一个较早的一个虚拟时间,撤销其间产生的一切附带影响,然后通过合适的顺序顺序执行这个迟到的消息并再次向前执行。

分布式系统中的回滚因这样的原因而复杂:要回滚的进程可能已经给其他进程发送了很多消息,而其他进程有可能因此产生其他事件,这就导致了多层次的附带影响。

反消息和回滚机制

进程运行时的组成如下:

  • 进程名:唯一的虚拟空间坐标
  • 逻辑时钟:虚拟时间坐标
  • 进程状态
  • 状态队列:进程状态的拷贝
  • 输入队列:包含按虚拟接收时间顺序到达的消息。输入队列中已被处理的消息并不删除,而是加一个负号(反消息)一遍将来回滚。
  • 输出队列:含有进程最近按虚拟发送时间顺序发送消息的反消息拷贝。在回滚时需要这些拷贝。

反消息:与消息内容相同,符号相反。传递消息时,该消息的一个拷贝进入接收方的输入队列,一个负拷贝留在发送方的输出队列以便发送方回滚。

当一个消息与其反消息出现在同一个队列中时,它们立刻无效。

回滚的触发:如果消息时间戳 &lt; &lt; < 接收方本地时间,则接收方必须进行回滚。

回滚机制

  1. 搜索状态队列,找出最大的并且 &lt; &lt; < 消息时间戳的状态,并从状态队列中去除该事件之后保存的所有状态,然后从该点恢复向前执行。为了收回一条消息,只需要发送其反消息即可。
  2. 考虑反消息的接收方:
  • 如果原消息已经到达,但还未被处理,则它的虚拟接收时间必定大于接收方的虚拟时间。反消息到来后不会引起回滚,它将直接与原消息一同无效。
  • 如果原消息已经开始被处理,反消息的到来会使得接收方回滚到原消息被接收的虚拟时间,然后废除原消息,使接收方不保留原消息的记录。这一回滚可能引发连串回滚。
  • 如果反消息先于原消息到达接收方,它只是被加入输入队列。接收方执行输入队列时将跳过反消息。

反消息的特点:健壮,无死锁。最坏的情况下,所有进程回滚到最初的虚拟时间,然后再向前运行。

3.8.5 全局控制机制

全局控制机制解决下面的问题:

  1. 回滚中系统的全局进展
  2. 全局终止检测
  3. 回滚过程中的错误和 I/O 处理
  4. 保存消息拷贝的内存开销
1. 全局虚拟时间(Global Virtual Time, GVT)

定义:在实际时间 r r r,全局虚拟时间是下面的较小值:

  • 在时间 r r r,所有虚拟时钟的所有虚拟时间
  • 在时间 r r r,已发送带还未被处理的所有消息的虚拟发送时间

两个重要性质:

  • 即使回滚,GVT 也不减。

  • 虚拟时间 &lt; &lt; < GVT 的事件不能回滚,可以被安全地提交。

有时间复杂度 O ( d ) O(d) O(d) 的 GVT 估计算法,其中 d d d 是广播的延迟。在虚拟时间系统执行期间,必须定期来估计 GVT。

2. GVT 的应用

(1) 内存管理和流控制

早于 GVT 的事件可以被提交,之后被销毁以避免内存开销。

溢出队列的消息被返回给发送者来撤销。

(2) 正常终止检测

进程结束时,虚拟时间被设置为 i n f inf inf,当 GVT 是 i n f inf inf 时,表明系统终止。

(3) 错误处理

(4) I/O

只有当消息的虚拟接收时间 &lt; &lt; < GVT,输出活动才能被执行。

(5) 快照和毁坏恢复

虚拟时间最广的应用是分布式离散事件模拟。

3.9 物理时钟同步:NTP

3.9.2 定义及术语

C a C_a Ca C b C_b Cb 是两个时钟

(1) 时间

在一个机器 p p p 中,时钟时间由函数 C p ( t ) C_p(t) Cp(t) 给出,对于一个理想的时钟, C p ( t ) = t C_p(t)=t Cp(t)=t

(2) 频率

频率是时钟的速度: C a ′ ( t ) C_a^{&#x27;}(t) Ca(t)

(3) 位移 (Offset)

时钟位移是时钟与实际时间之差: C p ( t ) − t C_p(t) - t Cp(t)t

(4) 偏离 (Skew)

时钟的偏离是时钟和理想时钟的频率差。时钟 C a C_a Ca 相对于 C b C_b Cb 的偏移是 C a ′ ( t ) − C b ′ ( t ) C_a^{&#x27;}(t) - C_b^{&#x27;}(t) Ca(t)Cb(t)

(5) 漂移 (Drift)

时钟的漂移是时钟的二阶导数: C a ′ ′ ( t ) C_a^{&#x27;&#x27;}(t) Ca(t)

3.9.3 时钟不确定性

规格: C a ′ ( t ) ∈ [ 1 − ρ , 1 + ρ ] ​ C_a^{&#x27;}(t) \in [1 - \rho, 1 + \rho]​ Ca(t)[1ρ,1+ρ]

3.10 本章小结

happens-before relation 是分布式程序设计和分析的基础。
分布式计算中的偏序事件集合与向量时间戳同构。

 

第四章 记录全局状态与快照算法

4.2 系统模型和定义

4.2.1 系统模型

进程 p i p_i pi 的本地状态由 L S i ​ LS_i​ LSi 表示,是进程直到此时执行的所有事件序列的结果。(事件集合的因果关系扩展到本地状态集合

C i j C_{ij} Cij 表示从 p i p_i pi p j p_j pj 的通道,它的状态为:
S C i j = t r a n s i t ( L S i , L S j ) = { m i j   ∣   s e n d ( m i j ) ∈ L S i   ∧   r e c ( m i j ) ∉ L S j } SC_{ij} = transit(LS_i, LS_j) = \{ m_{ij}\ |\ send(m{ij})\in LS_i\ \land\ rec(m_{ij}) \notin LS_j \} SCij=transit(LSi,LSj)={mij  send(mij)LSi  rec(mij)/LSj}

  • FIFO 模型:通道的作用是一个先进先出队列,消息的次序由通道维持
  • 非 FIFO 模型:通道的作用是一个发送方消息的集合,接收方从集合中随机接收消息
  • 因果依赖模型(CO): s e n d ( m i j ) → s e n d ( m k j )   ⇒   r e c ( m i j ) → r e c ( m k j ) ​ send(m_{ij})\to send(m_{kj})\ \Rightarrow\ rec(m_{ij})\to rec(m_{kj})​ send(mij)send(mkj)  rec(mij)rec(mkj)

通信网络模型:CO ⊂ \subset FIFO ⊂ \subset 非FIFO(随机)

4.2.2 一致性全局状态

分布式系统的全局状态是进程的本地状态和通道状态的集合,记为:
G S = { ∪ i L S i ,   ∪ i , j S C i j } GS = \{ \cup_iLS_i,\ \cup_{i,j}SC_{ij} \} GS={iLSi, i,jSCij}
全局状态是一个一致性全局状态,当且仅当满足下面2个条件:

  • 消息守恒 s e n d ( m i j ) ∈ L S i ⇒ m i j ∈ S C i j   ⊕   r e c ( m i j ) ∈ L S j send(m_{ij}) \in LS_i \Rightarrow m_{ij}\in SC_{ij}\ \oplus\ rec(m_{ij})\in LS_j send(mij)LSimijSCij  rec(mij)LSj,发送的消息要么出现在通道状态中,要么出现在接收方本地状态中(不论如何分割,本条件恒成立
  • 因果 s e n d ( m i j ) ∉ L S i ⇒ m i j ∉ S C i j   ∧   r e c ( m i j ) ∉ L S j send(m_{ij})\notin LS_i \Rightarrow m_{ij} \notin SC_{ij}\ \land\ rec(m_{ij}) \notin LS_j send(mij)/LSimij/SCij  rec(mij)/LSj,未发送的消息,既不会出现在通道状态中,也不会出现在接收方的本地状态中

不一致的全局状态是无意义的。

4.2.3 有关分割的解

在这里插入图片描述

一致性分割:在 PAST 接收的消息一定是在 PAST 发送。

图中分割线 C 1 C_1 C1 是不一致的; C 2 C_2 C2 是一致的。

4.2.4 记录全局快照遇到的问题

(1) 如何判别消息是否会被记录在快照中

快照前的发送消息一定会被记录在全局快照中。如果全局快照包含快照后的发送消息,则不一致。

(2) 如何确定进行快照的瞬间

如果 p j p_j pj 接收到的消息 m i j m_{ij} mij 是在 p i p_i pi 进行快照之后发送的,那么 p j ​ p_j​ pj 先进行快照,然后处理消息。

有两类消息:

  • 计算消息:通过应用程序交换
  • 控制消息:通过快照算法交换,对应用程序透明

4.3 FIFO 通道的快照算法

4.3.1 Chandy - Lamport 算法

L S i LS_i LSi p i p_i pi 记录;通道状态 S C i j SC_{ij} SCij 由接收方 p j ​ p_j​ pj 记录。

在这里插入图片描述

  1. 进程 p i p_i pi 开启快照
  • 记录本地状态 L S i ​ LS_i​ LSi
  • 开始记录所有通道 C k i C_{ki} Cki 的接收消息( S C k i SC_{ki} SCki 初始化为 ϕ \phi ϕ
  • 向每个输出通道 C i k ​ C_{ik}​ Cik 发送标记
  1. 进程 p i p_i pi 的标记发送规则
  • 记录本地状态 L S i ​ LS_i​ LSi

  • 向每个输出通道 C i k ​ C_{ik}​ Cik 发送标记

  1. 进程 p j p_j pj 的标记接收规则
  • p j p_j pj 从通道 C i j C_{ij} Cij 接收到标记时:

  • If p j ​ p_j​ pj 并未记录本地状态(即第一次接收标记

    • 记录 C i j C_{ij} Cij 的状态为空集,即 S C i j = ϕ SC_{ij} = \phi SCij=ϕ,这就是 S C i j SC_{ij} SCij
    • 开始记录通道 C k j ,   k ≠ i C_{kj},\ k\neq i Ckj, k̸=i 的接收消息( S C k j SC_{kj} SCkj 初始化为 ϕ \phi ϕ
    • 执行“标记发送规则”(与上面操作同步
  • Else即已经接收过标记

    • 停止记录 C i j ​ C_{ij}​ Cij 的消息(当然还是正常处理
    • 这就是 S C i j ​ SC_{ij}​ SCij

proof:

这翻译真是扯淡。。。

对于“消息守恒”条件:发送事件先于本地标记事件,根据FIFO模型,接收方会在本方发送的标记前收到消息。如果本方标记是接收方的第一个标记,那么接收事件一定属于接收方本地状态;否则本方标记不是接收方的第一个标记,那么消息要么属于通道状态,要么属于接收方本地状态。

对于“因果”条件:发送事件晚于本地标记事件,根据FIFO模型,接收方会在本方发送的标记后收到消息。如果本方标记是接收方的第一个标记,接收方本地快照,快照并未包含接受这个消息的操作,并且通道状态为空,于是消息一定不属于通道状态;否则本方标记不是接收方的第一个标记,通道状态的记录已经停止,不会记录到这个消息。

复杂度

算法需要 O ( e ) O(e) O(e) 个消息和 O ( d ) O(d) O(d) 时间,其中 e = ( n 2 ) e=\binom{n}{2} e=(2n) 为网络的边, d ​ d​ d 是网络的直径,即最大的两点最短距离。

参考:

4.3.2 被记录全局状态的性质

4.4 Chandy - Lamport 算法的变种

4.4.1 Spezialetti - Kearns 算法

将系统划分为若干个组,每个组内自己收集全局状态,组间不传播标记,也不做快照。然后每个组的启动者互相交换快照。(不过我没懂那组间的接收通道的状态怎么来?)

4.4.2 Venkatesan 快照增量算法

适用于需要重复收集系统的全局快照。

每个快照有版本号,修改了标记发送规则。(讲得很粗,不懂)

4.4.3 Helary 波同步方法

4.5 非 FIFO 通道的快照算法

在非FIFO模型中,通道中的消息以随机的方式被接收。需要采取其他技术保证“因果”条件被满足。

4.5.1 Lai - Yang 算法

(1) 每个进程开始是白色的,快照时变为红色。红色进程执行“标记发送规则”

(2) 进程发送的消息的颜色与进程相同。因此,白色消息是快照前发送,红色消息是快照后发送。

(3) 每一个白色进程必须在接收第一个红色消息之前快照

这保证了一个进程在记录了本地快照之后发送的消息(红),不会被未进行快照的接收进程处理。(保证“因果”条件)

FIFO模型自动区分快照前后的消息,但是非FIFO模型不行,必须给消息加入一个tag,用来区分快照前后发送的消息。

(4) 每个白色进程记录了每个通道发送或接收白色消息的历史

(5) 进程变红时,它把历史和自己的快照一起发送给收集全局快照的启动者

(6) 启动者评估 t r a n s i t ( L S i , L S j ) transit(LS_i, LS_j) transit(LSi,LSj),计算 C i j C_{ij} Cij 的状态:
S C i j = p i   在   C i j   上 发 送 的 白 消 息   −   p j   在   C i j   上 接 收 的 白 消 息 = { m i j   ∣   s e n d ( m i j ) ∈ L S i }   −   { m i j   ∣   r e c ( m i j ) ∈ L S j } \begin{aligned} SC_{ij} &amp; = p_i\ 在\ C_{ij}\ 上发送的白消息\ -\ p_j\ 在\ C_{ij}\ 上接收的白消息 \\ &amp; = \{ m_{ij}\ |\ send(m_{ij})\in LS_i \}\ -\ \{ m_{ij}\ |\ rec(m_{ij})\in LS_j \} \\ \end{aligned} SCij=pi  Cij   pj  Cij ={mij  send(mij)LSi}  {mij  rec(mij)LSj}
proof:

  • “消息守恒”条件成立:白色消息要么被接收,要么在通道状态中。

  • “因果”条件成立:因为红色消息既不在通道状态中,也不在本地状态中。

4.5.2 Li 等人的算法
4.5.3 Mattern 算法

基于向量时钟,通过向量时间戳来判断,在非FIFO模型中,一个发送消息是在快照之前还是之后。(不可比呢??好像是只比较启动者的那一维时间,所以相当于一个tag标记是否已经快照,换句话说一定可比。。。)

4.6 因果传递系统快照

CO 的性质非常强: s e n d ( m i j ) → s e n d ( m k j )   ⇒   r e c ( m i j ) → r e c ( m k j ) send(m_{ij})\to send(m_{kj})\ \Rightarrow\ rec(m_{ij})\to rec(m_{kj}) send(mij)send(mkj)  rec(mij)rec(mkj)

4.6.1 进程状态记录

这两个算法用相同的原理记录本地状态:启动者广播一个标志 t o k e n token token(包括自己),进程 p i p_i pi 接收到 t o k e n token token 的副本 t o k e n i token_i tokeni 时,记录本地快照 L S i ​ LS_i​ LSi,并发送给启动者。当启动者收到每个进程的快照时,算法终止。

4.6.2 Acharya - Badrinath 算法中的通道状态记录

每个进程 p i p_i pi 维护:

  • S E N T i [ 1.. n ] SENT_i[1..n] SENTi[1..n] S E N T i [ j ] SENT_i[j] SENTi[j] p i p_i pi 发给 p j p_j pj 的消息集合
  • R E C D i [ 1.. n ] RECD_i[1..n] RECDi[1..n] R E C D i [ j ] RECD_i[j] RECDi[j] p i p_i pi 接收 p j p_j pj 的消息数集合

当进程 p i p_i pi 收到 t o k e n i token_i tokeni 记录本地快照时,将数组 S E N T i , R E C D i SENT_i, RECD_i SENTi,RECDi 和本地快照一并发给启动者。当算法终止时,启动者按下述方法确定通道状态:

(1) 从启动者到每个进程的通道状态为空

(2) S C i j SC_{ij} SCij 是由 S E N T i [ j ] − R E C D j [ i ] SENT_i[j] - RECD_j[i] SENTi[j]RECDj[i] 给出的消息集合(注意:并不一定有 R E C D j [ i ] ⊆ S E N T i [ j ] ​ RECD_j[i] \subseteq SENT_i[j]​ RECDj[i]SENTi[j]

proof:

对于“消息守恒”条件:

对于一个 s e n d ( m i j ) ∈ L S i send(m_{ij})\in LS_i send(mij)LSi

  • 如果 p i p_i pi 是启动者,那么一定有 S C i j = ϕ   ∧   r e c ( m i j ) ∈ L S j SC_{ij} = \phi\ \land\ rec(m_{ij})\in LS_j SCij=ϕ  rec(mij)LSj,因为 s e n d ( m i j ) → s e n d ( t o k e n j ) ⇒ r e c ( m i j ) → r e c ( t o k e n j ) send(m_{ij}) \to send(token_j) \Rightarrow rec(m_{ij}) \to rec(token_j) send(mij)send(tokenj)rec(mij)rec(tokenj)
  • 否则要么 r e c ( m i j ) ∈ L S j rec(m_{ij})\in LS_j rec(mij)LSj,要么 m i j ∈ S E N T i [ j ] − R E C D j [ i ] m_{ij}\in SENT_i[j]-RECD_j[i] mijSENTi[j]RECDj[i]

对于“因果”条件:

对于一个 s e n d ( m i j ) ​ send(m_{ij})​ send(mij) 事件,使得 s e n d ( t o k e n ) → r e c ( t o k e n i ) → s e n d ( m i j ) ​ send(token) \to rec(token_i) \to send(m_{ij})​ send(token)rec(tokeni)send(mij)

  • s e n d ( m i j ) ∉ S C i j send(m_{ij}) \notin SC_{ij} send(mij)/SCij,因为 s e n d ( m i j ) ∉ S E N T i [ j ] send(m_{ij}) \notin SENT_i[j] send(mij)/SENTi[j]
  • r e c ( t o k e n j ) → r e c ( m i j ) ⇒ r e c ( m i j ) ∉ L S j rec(token_j) \to rec(m_{ij}) \Rightarrow rec(m_{ij}) \notin LS_j rec(tokenj)rec(mij)rec(mij)/LSj

这里应该是要假设 “所有 s e n d ( t o k e n x ) send(token_x) send(tokenx) 在同一瞬间完成”

4.6.3 Alagar - Venkatesan 算法中的通道状态记录

定义 s e n d ( m i j ) → s e n d ( t o k e n x ) send(m_{ij}) \to send(token_x) send(mij)send(tokenx);否则称为

按下述方法记录通道状态:

(1) 当进程接收 t o k e n token token 时,进行本地快照,返回 D o n e ​ Done​ Done 消息给启动者。并开始记录接收通道上的消息

(2) 启动者收到所有 D o n e Done Done 消息后,广播一个终止消息( s e n d ( s t o p x ) send(stop_x) send(stopx)

(3) 当收到终止消息,进程停止记录通道状态

proof:

首先注意到一个事实: s e n d ( t o k e n x ) → r e c ( t o k e n i ) → s e n d ( D o n e i ) → s e n d ( s t o p x ) → r e c ( s t o p i ) send(token_x) \to rec(token_i) \to send(Done_i) \to send(stop_x) \to rec(stop_i) send(tokenx)rec(tokeni)send(Donei)send(stopx)rec(stopi)

对于“消息守恒”条件:

对于一个 s e n d ( m i j ) ∈ L S i send(m_{ij}) \in LS_i send(mij)LSi,有 s e n d ( m i j ) → s e n d ( s t o p j ) ⇒ r e c ( m i j ) → r e c ( s t o p j ) send(m_{ij}) \to send(stop_j) \Rightarrow rec(m_{ij}) \to rec(stop_j) send(mij)send(stopj)rec(mij)rec(stopj),于是要么 r e c ( m i j ) ∈ L S j rec(m_{ij}) \in LS_j rec(mij)LSj,要么 m i j ∈ S C i j m_{ij} \in SC_{ij} mijSCij

对于“因果”条件:

对于一个 s e n d ( m i j ) send(m_{ij}) send(mij) 事件,使得 r e c ( t o k e n i ) → s e n d ( m i j ) rec(token_i) \to send(m_{ij}) rec(tokeni)send(mij)

  • 注意到 s e n d ( m i j ) send(m_{ij}) send(mij)消息,所以 m i j ∉ S C i j m_{ij} \notin SC_{ij} mij/SCij
  • s e n d ( t o k e n j ) → s e n d ( m i j ) ⇒ r e c ( t o k e n j ) → r e c ( m i j ) send(token_j) \to send(m_{ij}) \Rightarrow rec(token_j) \to rec(m_{ij}) send(tokenj)send(mij)rec(tokenj)rec(mij) 表明 r e c ( m i j ) ∉ L S j rec(m_{ij}) \notin LS_j rec(mij)/LSj

快照算法的比较

在这里插入图片描述

4.8 一致性全局快照的必要和充分条件

检查点进程的中间状态,可以认为没有事件。用 C p , i C_{p,i} Cp,i 表示进程 p p p_p pp 的第 i i i 个检查点。 C p , i C_{p,i} Cp,i 所代表的区间由 C p , i − 1 C_{p,i-1} Cp,i1 C p , i ​ C_{p,i}​ Cp,i 的所有事件组成。

全局快照就是每个进程上一个检查点的集合。如果全局快照中任何两个检查点之间没有 happens-before relation,则这个全局快照是一致性的。

4.8.1 Zigzag 路径和一致性全局快照
1. Zigzag 路径

定义1:从检查点 C x , i C_{x,i} Cx,i C y , j C_{y,j} Cy,j 存在一条 z i g z a g zigzag zigzag 路径,当且仅当存在消息 m 1 , . . . , m q ​ m_1,...,m_q​ m1,...,mq,使得

(1) m 1 m_1 m1 被进程 p x p_x px C x , i C_{x,i} Cx,i 之后发送

(2) 如果 m k   ( k ∈ [ 1 , q − 1 ] ) m_k\ (k\in[1,q-1]) mk (k[1,q1]) p z p_z pz 接收,那么 m k + 1 m_{k+1} mk+1 在相同的(或之后)检查点区间被 p z p_z pz 发送。( s e n d ( m k + 1 ) send(m_{k+1}) send(mk+1) r e c ( m k ) rec(m_k) rec(mk) 先后关系未指定!!换句话说,如果 s e n d ( m k + 1 ) send(m_{k+1}) send(mk+1) 在前,那么一定在同一个检查点区间内;如果 s e n d ( m k + 1 ) ​ send(m_{k+1})​ send(mk+1) 在后,则没有要求,自然同步

(3) m q ​ m_q​ mq 被进程 p y ​ p_y​ py C y , j ​ C_{y,j}​ Cy,j 之前接收

在这里插入图片描述

例如: C 1 , 1 C_{1,1} C1,1 C 3 , 2 C_{3,2} C3,2 z i g z a g zigzag zigzag 路径经过 m 3 m_3 m3 m 4 m_4 m4。(进程 p 2 p_2 p2 在同一检查点区间内, r e c ( m 3 ) rec(m_3) rec(m3) 并且 s e n d ( m 4 ) send(m_4) send(m4),另外 s e n d ( m 4 ) send(m_4) send(m4) 先于 r e c ( m 3 ) rec(m_3) rec(m3)

定义2:一个检查点 C C C 被包含在一个 z i g z a g zigzag zigzag 环路中,当且仅当存在一条从 C C C 到本身的 z i g z a g ​ zigzag​ zigzag 路径。

在这里插入图片描述

例如: C 2 , 1 C_{2,1} C2,1 是在由消息 m 1 m_1 m1 m 2 m_2 m2 形成的一条 z i g z a g zigzag zigzag 环路中。(消息序列为 m 2 , m 1 m_2, m_1 m2,m1;在进程 p 2 p_2 p2 中, m 2 m_2 m2 在检查点 C 2 , 1 C_{2,1} C2,1 之后发送;在进程 p 1 p_1 p1 中,同一检查点内 s e n d ( m 1 ) send(m_1) send(m1) 先于 r e c ( m 2 ) rec(m_2) rec(m2);最后在进程 p 2 p_2 p2 中, m 1 m_1 m1 C 2 , 1 C_{2,1} C2,1 之前被接收)

2. Zigzag 路径和因果路径之间的不同

因果路径:存在一条从检查点 A A A 到检查点 B B B 的因果路径,当且仅当存在一条在 A A A 之后开始,在 B B B 之前结束的消息链,该链中前一个消息被接收后,后一个消息才被发送。因果路径是 z i g z a g zigzag zigzag 路径。

z i g z a g zigzag zigzag 路径中,消息链的前一个消息接收之前,后一个消息允许已经被发送,只要在同一个检查点区间内。

3. 一致性全局快照

z i g z a g zigzag zigzag 路径的性质:任何包含这两个检查点的快照都是不一致性的。

Netzer 和 Xu 证明:如果检查点集合 S S S 是一个一致性全局快照,当且仅当 S S S 中任意两点不存在 z i g z a g ​ zigzag​ zigzag 路径。

在这里插入图片描述

总结:

  • 在快照的检查点之间没有因果路径是一致性快照的必要条件
  • 在快照的检查点之间没有 z i g z a g zigzag zigzag 路径是一致性快照的充分必要条件
  • 一个检查点能够成为一致性快照的一部分,当且仅当该检查点不属于 z i g z a g zigzag zigzag 环路

4.9 找出分布式计算中的一致性全局快照

定义: A ​ A​ A B ​ B​ B 是检查点, R ​ R​ R S ​ S​ S 是检查点集合,令 ⇝ ​ \leadsto​ 是检查点和检查点集合间的关系

(1) A ⇝ B A\leadsto B AB,当且仅当存在从 A A A B B B 的 Z 路径

(2) A ⇝ S A\leadsto S AS,当且仅当存在从 A A A S S S 的某个成员的 Z 路径

(3) S ⇝ A S\leadsto A SA,当且仅当存在一条从 S S S 的某个成员到 A A A 的 Z 路径

(4) R ⇝ S ​ R\leadsto S​ RS,当且仅当存在一条从 R ​ R​ R 的某个成员到 S ​ S​ S 的某个成员的 Z 路径

定理:检查点集合 S S S 能被扩展成一个一致性全局快照,当且仅当 S ̸ ⇝ S S\not\leadsto S S̸S

4.9.1 找出一致性全局快照

S S S 是使得 S ̸ ⇝ S S\not\leadsto S S̸S 的检查点的一个集合,那么对进程 p q p_q pq,集合 S u s e f u l q S_{useful}^q Susefulq 被定义为
S u s e f u l q = { C q , i   ∣   ( S ̸ ⇝ C q , i )   ∧   ( C q , i ̸ ⇝ S )   ∧   ( C q , i ̸ ⇝ C q , i ) } S_{useful}^q = \{ C_{q,i}\ |\ (S\not\leadsto C_{q,i})\ \land\ (C_{q,i}\not\leadsto S)\ \land\ (C_{q,i}\not\leadsto C_{q,i}) \} Susefulq={Cq,i  (S̸Cq,i)  (Cq,i̸S)  (Cq,i̸Cq,i)}
之后定义 $S_{useful} = \bigcup_q S_{useful}^q $

引理:令 S S S 是使得 S ̸ ⇝ S S\not\leadsto S S̸S 的检查点的一个集合,并且 C q , i ∉ S C_{q,i}\notin S Cq,i/S。那么 S ∪ { C q i } S\cup \{C_{q_i}\} S{Cqi} 能扩展成一个一致性快照,当且仅当 C q , i ∈ S u s e f u l C_{q,i}\in S_{useful} Cq,iSuseful

定理:令 S ​ S​ S 是使得 S ̸ ⇝ S ​ S\not\leadsto S​ S̸S 的检查点的一个集合,且 T ​ T​ T 是任意检查点集合满足 T ∩ S = ϕ ​ T \cap S = \phi​ TS=ϕ。那么 S ∪ T ​ S\cup T​ ST 是一个一致性全局快照,当且仅当

  • T ⊆ S u s e f u l T\subseteq S_{useful} TSuseful
  • T ̸ ⇝ T T\not\leadsto T T̸T
  • ∣ S ∪ T ∣ = N ​ |S\cup T| = N​ ST=N
4.9.2 枚举式一致性快照 Manivannan - Netzer - Singhal 算法

C o m p u t e A l l C g s ( S )   { let   G = ϕ if   S ̸ ⇝ S   then 设   A l l P r o s   表 示 未 在 集 合   S   中 出 现 的 进 程 C o m p u t e A l l C g s F r o m ( S ,   A l l P r o s ) return   G } C o m p u t e A l l C g s F r o m ( S , P r o c S e t )   { if   ( P r o c S e t = ϕ )   then G = G ∪ { S } else   设   P q   表 示 集 合   P r o c S e t   中 的 任 一 进 程 for   ∀   C ∈ S u s e f u l q   do   C o m p u t e A l l C g s F r o m ( S ∪ { C } ,   P r o c S e t − { P q } ) } \begin{aligned} &amp; ComputeAllCgs(S)\ \{ \\ &amp; \qquad \textbf{let}\ G = \phi \\ &amp; \qquad \textbf{if}\ S\not\leadsto S\ \textbf{then} \\ &amp; \qquad \qquad 设\ AllPros\ 表示未在集合\ S\ 中出现的进程 \\ &amp; \qquad \qquad ComputeAllCgsFrom(S,\ AllPros) \\ &amp; \qquad \textbf{return}\ G \\ &amp; \} \\ &amp; \\ &amp; ComputeAllCgsFrom(S, ProcSet)\ \{ \\ &amp; \qquad \textbf{if}\ (ProcSet = \phi)\ \textbf{then} \\ &amp; \qquad \qquad G = G\cup \{S\} \\ &amp; \qquad \textbf{else}\ \\ &amp; \qquad \qquad 设\ P_q\ 表示集合\ ProcSet\ 中的任一进程 \\ &amp; \qquad \qquad \textbf{for}\ \forall\ C\in S_{useful}^q\ \textbf{do}\ \\ &amp; \qquad \qquad \qquad ComputeAllCgsFrom(S\cup\{C\},\ ProcSet - \{P_q\}) \\ &amp; \} \\ \end{aligned} ComputeAllCgs(S) {let G=ϕif S̸S then AllPros  S ComputeAllCgsFrom(S, AllPros)return G}ComputeAllCgsFrom(S,ProcSet) {if (ProcSet=ϕ) thenG=G{S}else  Pq  ProcSet for  CSusefulq do ComputeAllCgsFrom(S{C}, ProcSet{Pq})}

C o m p u t e A l l C g s ( S ) ComputeAllCgs(S) ComputeAllCgs(S) 返回包含 S S S 的所有一致性快照的集合。(即 G G G 中的每一个元素都是包含 S S S 的一致性快照)

C o m p u t e A l l C g s F r o m ( S , P r o c S e t ) ComputeAllCgsFrom(S, ProcSet) ComputeAllCgsFrom(S,ProcSet) 通过递归,试图从每一个可扩展的检查点向下遍历。

proof:略。

4.9.3 在分布式计算中找出 Z 路径

定义:分布式计算的回滚依赖图(R图)是一个有向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 是检查点集合,边 ( C p , i , C q , j ) ∈ E (C_{p,i}, C_{q,j})\in E (Cp,i,Cq,j)E 如果

(1) p = q   ∧   j = i + 1 p=q\ \land\ j=i+1 p=q  j=i+1

(2) p ≠ q p\neq q p̸=q C p , i C_{p,i} Cp,i 区间发送的消息被 C q , j ​ C_{q,j}​ Cq,j 区间接收

构建一个R图

在这里插入图片描述

定理:令 G = ( V , E ) G=(V,E) G=(V,E) 是R图,那么对任意两个检查点 C p , i C_{p,i} Cp,i C q , j C_{q,j} Cq,j,有 C p , i ⇝ rd C q , j C_{p,i} \overset{\text{rd}}{\leadsto} C_{q,j} Cp,irdCq,j 当且仅当

(1) p = q   ∧   i , j ​ p=q\ \land\ i,j​ p=q  i,j

(2) C p , i + 1 ⇝ rd C q , j C_{p,i+1}\overset{\text{rd}}{\leadsto} C_{q,j} Cp,i+1rdCq,j(可能有 p = q p=q p=q

4.10 本章小结

记录全局快照的一个重点在于区分本地快照前后发送的消息,在不同通信模型下会有不一样的处理。

 

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值