向量时钟算法

1,930 阅读6分钟

问题思考:如何确定分布式系统中事件的因果关系?

向量时钟算法是在分布式系统中,描述分布式系统中时序和因果关系的一种机制。为了刻画时序,我们很容易联想到时钟,然而,由于网络延迟、时钟误差等现实问题,我们很难建立一个全局的时钟来描述时序,因此区别于物理时钟的逻辑时钟应运而生吗。向量时钟就是一种逻辑时钟算法,可以描述分布式系统中事件的因果关系

在计算机系统中,有两种处理时间的方式,物理时钟和逻辑时钟。

物理时钟跟我们真实的时间对应,很直观,而且使用方便,但是无法做到绝对精确。逻辑时钟是相对时间,当计算机系统发生事件交互时,事件发送的先后就是逻辑时钟对应的时间顺序。

本文从计算机系统的物理时钟和逻辑时钟入手,主要介绍向量时钟算法的基本过程和向量时钟的应用场景。

  • 物理时钟的原理

  • 逻辑时钟的原理

  • 向量时钟算法原理和应用

物理时钟

物理计时

物理时钟是使用物理电路来计时的,在普通的计算机的主板上有一个石英晶体振荡器和一个纽扣电池,石英晶体振动一定的频率后,就表示1秒钟到了。但是,石英晶体计时是存在误差的,每天的计时误差在正负1秒钟,而且在极端温度下,比如零下二十度,误差会变大。为了避免物理时钟的时间误差,NTP协议被设计出来同步网络时间。

NTP协议

NTP协议全名网络时间协议,是被设计来用于同步网络时间的协议,使网络中的计算机时钟同步到UTC。提供NTP对时的服务器有很多,比如微软的NTP对时服务器,利用NTP服务器提供的对时功能,可以使我们的设备时钟系统能够正确运行。NTP****协议时间同步也是存在误差的,实际上广域网可以达到几十毫秒的误差,局域网误差可以在1毫秒内。

NTP时间服务器是一种主从式架构协议,使用分层的时钟源系统,其中,阶层0是参考时钟, 这些是高精度计时设备,例如原子钟(如铯、铷)、GPS时钟或其他无线电时钟,阶层的上限是15,阶层16表示未同步设备。

NTP协议是一种UDP 协议,使用123端口。典型的NTP客户端将定期轮询不同网络上的三个或更多服务器。为同步其时钟,客户端必须计算其时间偏移量和来回通信延迟,并根据时间偏移量θ和来回通信延迟δ来调整自身时间。θ和δ的值通过过滤器并进行统计分析。异常值被剔除,并从最好的三个剩余候选中导出估算的时间偏移。然后调整时钟频率以逐渐减小偏移,创建一个反馈回路。

时间偏移θ定义为:

往返延迟δ为:

逻辑时钟

在分布式系统中,不同的服务部署在不同的机器上,那么如何确定不同机器上的两个事件发生的先后顺序呢。使用物理时钟和逻辑时钟都是可以描述事件的顺序的。但是物理时钟是存在时间误差的,现在也有一些算法可以进一步较小误差,提高精确度,但是实现成本相对较高,使用逻辑时钟也可以解决这个问题。逻辑时钟并不度量时间本身,仅区分事件发生的前后顺序。

逻辑时间顺序

在分布式系统中,节点交互可分为三种事件:

  • 节点内部事件

  • 发送事件

  • 接收事件

分布式系统的逻辑时钟,最主要是确定上面三种事件的时间先后顺序。

我们把事件a发生在b之前定义为 a → b,有三种条件满足a → b

  • **(物理时间判断)**a和b是同一个进程内的事件,a发送在b之前,则a → b

  • **(逻辑顺序判断)**a和b在不同的进程中,但是a是发送进程的发送事件,b是同一个消息的接收进程的接收事件,则a → b

  • **(先后传递)**如果a → b并且b → c,则a → c

另外,如果我们没有办法确定a和b的先后关系,则称两个事件是并发的。

根据上图我们可以得出下面的先后顺序

  • a → b → c → d

  • a → b → e

  • f → c → d

下面的事件是并发执行的,没有办法确定先后顺序

  • a || f

  • e || d

  • b || f

  • e || c

Lamport逻辑时钟

Lamport逻辑时钟是一种简单的逻辑时钟算法,构建了一个全序时钟来描述事件顺序,用来确定分布式系统中的事件顺序,即定义a事件在b事件之前发送。Lamport逻辑时钟算法是向量时钟的基础

算法基本规则

  1. 每个事件对应一个Lamport时间戳,初始值为0
  2. 如果事件是节点内部事件,本地进程中的时间戳加1
  3. 如果事件是发送事件,本地进程中的时间戳加1并在消息中带上该时间戳
  4. 如果事件是接收事件,本地进程中的时间戳 = Max(本地时间戳,消息中的时间戳) + 1

发送进程

time = time + 1;
send(message, time);

接收进程

(message, time_stamp) = receive();
time = max(time_stamp, time) + 1;

两个不同的事件a,b在同一个进程执行时,需要保证C(a) != C(b),因此需要保证下面两个条件。

  • 同一个进程内需要有一个计数器来递增事件的时钟

  • 多进程(线程)环境下,需要把进程时间区分开,保证事件的clock不相等

算法推导

假设有两个事件a和b,C(a)、C(b)分别表示a和b事件的Lamport时间戳

从上面的算法可以得出这两个结论

  • 同一个进程内的两个事件a和b,如果 a → b,那么 Ci (a) < Ci (b)

  • a是Pi进程的消息发送事件,b是Pj进程该消息的接收事件,那么 Ci (a) < Cj (b)

那么如果 a → b,则有C(a) < C(b)

然而,如果C(a) < C(b) 并不能推导出 a → b

另外,如果 C(a) = C(b),我们可以确定a和b一定没有因果关系,但是并不能确定他们的执行先后顺序。

Lamport逻辑时钟使用了一个全局时钟来定义了事件发生的先后顺序,然而,如果两个事件并不相关,那么这个时钟给出的大小关系就没有意义了。它不能描述事件的因果关系,导致即使知道了两个逻辑时钟值,但却不能确定这两个事件的因果关系。

向量时钟

向量时钟是对Lamport的时钟的一个扩展实现,算法结构一致,只是多传了一部分信息,通过这些信息,我们可以推导出事件发生的因果关系。向量时钟用来确定分布式系统内事件的偏序因果关系,在向量时钟算法中如果C(a) < C(b) 可以推导出 a → b 。

算法基本规则

在向量时钟算法中,进程在进行事件交互时,发送的消息不仅需要同步本进程的时钟值,还需要同步自己知道的其他进程的时钟值。

算法如下:

每个进程Pi会维护一个本地逻辑时钟向量VCi,向量的长度是分布式系统中进程的总个数。VCi (j) 表示进程Pi知道的进程Pj的本地逻辑时钟值。

  1. 初始化VCi的值全为0:VCi = [0, … , 0]
  2. 进程Pi每发生一次事件,VCi[i]加1
  3. 进程Pi给进程Pj发送消息,需要带上自己的向量时钟VCi
  4. 进程Pj接收消息,需要做两步操作
  • 对于VCj向量中的每个值VCj[k],更新为 max (VCi[k], VCj[k])。
  • 将VCj中自己对应的时钟值加1,即VCj[j]加1

算法推导

首先定义VCi的大小关系

  • 如果向量VCi中的每个元素VCi[k]都小于等于VCj中的对应元素VCj[k],则VCi VCj

  • 如果VCi中的每个元素VCi[k]都和VCj中的对应元素VCj[k]相等,则VCi = VCj

  • 如果VCi和VCj不能比较大小,则称两个向量是并发

从以上算法可以很容易地得出下面两个结论:

  1. 同一个进程内的两个事件a和b,如果 a → b,那么 VCi (a) < VCi (b)
  2. a是Pi进程的消息发送事件,b是Pj进程该消息的接收事件,那么 VCi (a) < VCj (b)

那么

对于任意两个事件a和b,如果 a → b,那么 VC (a) < VC (b)

对于VC(a) < VC(b) ,能不能得出 a → b呢?

这里我们可以简单推导一下:

  • 如果事件a和b在同一个进程内,很显然 a → b

  • 如果事件a和b在不同进程内,比如Pa和Pb,那么可以得出以下结论

设VCa = [m ,n], VCb = [s, t],因为VCa < VCb,所以m s,n t

那么一定在不早于a之前和不晚于b之后的时间内,Pa向Pb发送了消息

可以细分为以下几种情况:

根据以上推导,可以得出

如果 a → b,那么 VC (a) < VC (b)

如果 VC (a) < VC (b),那么a → b

向量时钟算法中,定义的向量时钟可以准确刻画事件顺序,我们可以通过事件的向量时钟来确定分布式系统中事件发生的先后。

算法应用

向量时钟算法可以记录事件的因果关系,那么我们可以使用这个算法来解决分布式系统中的数据版本校验问题。

我们假设有一个三副本的存储系统

A、B、C是分布式数据库的三个副本。

在T(A1)时刻,A提交了修改Key=Value1,并且数据同步到B和C上

在T(B1)时刻,B数据库提交了修改Key=Value2并同步到C上

C由于网络原因,先收到了Key=Value2的请求,后面才收到Key=Value1的请求。

如果使用先到先更新的逻辑,那么分布式数据库就会出现数据不一致的情况了。那么如何解决这个问题呢。

这里我们使用向量时钟算法,给每个事件加上时钟,此时C节点中,因为(1,1,0)>(1,0,0),我们推出事件的最后修改事件应该是(1,1,0),因此C数据库中,会更新Key=Value2。

向量时钟算法利用向量将全局各个进程的逻辑时间广播给各个进程,通过向量时间来比较任意两个事件的因果关系,可以用来解决数据冲突检测、强制因果通信等需要判断事件因果关系的问题。

参考资料

www.eecis.udel.edu/~mills/data…

en.wikipedia.org/wiki/Lampor…

zh.wikipedia.org/wiki/%E7%B6…

lamport.azurewebsites.net/pubs/time-c…

yang.observer/2020/09/12/…

[github.com/davideuler/…github.com/davideuler/… 朋友圈技术之道wechat_moments_architecture_201512.pdf)