今天的文章来聊聊向量时钟,在前文介绍分布式系统一致性的时候,曾经介绍过,在弱一致性模型当中会有一个因果性的问题 。向量时钟算法正是设计出来解决因果关系问题的 。
我们来回顾一下因果问题,在实际日常的网页行为当中,部分行为存在因果关系 。比方说知乎里面回答问题,显然得先有一个同学提出问题,然后才能有各路大V谢邀解答问题 。但是由于是分布式系统,有可能问题和回答并不是存放在同一台机器,导致有可能它们更新的顺序不一致,所以就有可能会出现用户在访问知乎的时候,发现自己关注的大V回答了某个问题,但是点进去问题却是空的 。这种幽灵情况不是灵异事件,只是单纯的分布式系统设计没过关,没有考虑因果问题 。
有的同学可以会说,这个不难啊,我们加入时间戳啊 。时间戳的确可以解决一部分问题,但是并不能解决所有问题 。有了时间戳之后,我们可以获得事件发生的时间,但是仍然不知道不同的数据之间的因果关系 。由于分布式系统存在延迟,也不能简单地通过时间戳来做过滤或者筛选 。不过,虽然单纯的时间戳不行,但已经非常接近了 。
我们日常生活当中用事件发生的时间来反应事物发生的顺序,我们说的先后顺序,其实是以客观上的时间作为参考系参考得到的结果 。问题来了,我们能不能找到或者构造出其他的参考系来反应事物发生的顺序呢?
当然是可以的,不然也没有这篇文章了,这就是大名鼎鼎的逻辑时钟算法 。多说一句,逻辑时钟算法和许多其他分布式算法一样,同样源于大神Lamport的发明 。
逻辑时钟
我们还用之前的例子来思考一下,一个人在知乎提交了问题,另一个人回答了问题,这是两个事件 。我们第一反应自然是通过两个事件发生的时间来反应因果顺序,但我们仔细分析一下这个场景 。后面那个人既然能回答问题,说明他一定是看到了问题 。也就是说回答问题和看到问题之间发生了交互,所以很自然地可以想到,我们是不是可以用两个系统或者是两个事件之间有没有发生过信息交互来反应因果顺序呢?
于是基于这个思想,Lamport大神提出了逻辑时钟的概念 。逻辑时钟概念的核心就是刚才我们说的,两个事件之间建立因果关系的前提是,两个事件之间发生过信息传递 。
我们梳理一下可能发生的事件的种类,可以分成三种 。第一种是发生在某个节点内部,也就是说没有和其他任何节点发生联系 。第二种是发送事件,是事件的发送方 。第三种是接收事件,和第二种对应,是事件的接收方 。明确了这三点之后,我们就可以用时间戳来表示这三种情况了 。首先,我们假设每个节点内部都会维护一个时间戳,记录当下节点的状态 。
针对上面说是三种时序关系呢,我们设定三种策略 。
首先是内部事件,对于节点内部发生事件呢,很简单,我们只需要将它的时间戳增大1,表示发生过了某件事情 。
其次是发送事件,节点内部的时间戳自增1,并且在发送消息当中加上这个时间戳 。
最后是接收事件,由于会额外接收到一个时间戳,所以我们需要利用这个时间戳来更新节点内部的时间戳 。更新的方法也很简单,假设节点内部的时间戳是t,跟着消息传递而来的时间戳是t', 那么: t_new = max(t + 1, t')。
我们分析一下上面这个关系,假设当下有事件A和B,如果事件A是事件B发生的前提 。那么显然事件A的时间戳小于事件B 。如果反过来,事件A的时间戳小于B,能说明事件A是事件B的前提吗?并不能,所以时间戳较小是因果关系的必要条件,但不是充分条件 。
由于会存在多个节点或者进程时间戳相等的情况,所以我们把进程id也作为比较的银子 。我们用C表示一个事件的时间戳,P表示事件的进程pid 。如果事件A排在事件B前面,只有两种可能:
- C(A) < C(B)
- C(A) = C(B) and P(A) < P(B)
我们来看一个例子:

文章插图
上图当中有A、B和C三个进程,其中P(A) < P(B) < P(C) 。图中每一个箭头都代表传递的消息 。
我们根据重新定义的时序关系,可以得到这些点的先后顺序是:
C1⇒B1⇒B2⇒A1⇒B3⇒A2⇒C2⇒B4⇒C3⇒A3⇒B5⇒C4⇒C5⇒A4
推荐阅读
- 俄罗斯的黑客技术堪称世界一流,原因是什么,经过对比恍然大悟
- Win7换Win10不懂就亏大了!Win10的隐藏秘技
- 如何区分Linux中的源码包和二进制包
- 让PHP7达到最高性能的几个Tips
- 利用宝塔自建linux+nginx-rtmp-module直播服务器的正确方法
- 有暖气的卫生间还需要浴霸吗,浴霸用风暖好还是灯暖好
- 最详细的的IP报头注释
- Win10 WSL配置centos的运行环境
- 冰箱冷藏室结冰和封条有关吗,冰箱冷藏室结冰是封条的问题吗
- 2020年最受JavaScript开发者欢迎的IDE
