一. 前言
Vector Clock是继Lamport Clock之后的改进版本,对逻辑时钟进行了一些调整使其更便于使用,也更适合实际的分布式系统场景。本文介绍了向量时钟的算法,并加以举例说明向量时钟依然存在的问题。
二. Vector Clock
相较于Lamport Clock,向量时钟最大的特点在于记录逻辑时间时分别记录每一个节点的当前逻辑时间,从而解决了并发时序问题。
vector时钟算法:
- 初始化所有的时钟为0
- 每一次处理内完内部事件,将自己的logic clock 加1
- 每一次发送一个消息的时候,需要将自己的向量时钟和消息一起发送
- 每一次接收到一个消息的时候,需要将自己的 logic clock +1,同时更新每一个逻辑时钟,更新的规则是取出收到 向量时钟和自己的取最大值
如下图所示为简单示例:
图中红色部分表示逻辑时间的更新。由此可见每个节点依次存储了A, B, C的时序,从而解决了分布式的时序问题。结合下面这个例子可以更清楚地明白向量时钟的算法过程。
假设Alice组织几位好友一起出游。提出了周三出游的建议。
Ben收到消息后,给出了周二如何的建议。
Dave收到Ben的消息后,觉得周二也可以,于是就回复Ben可以接受周二。
但是Cathy收到Alice的消息后,犹豫了一会觉得周四更好,并发消息给Dave。
这时候Dave就收到了两条无法回溯的消息:
date = Tuesday
vclock = Alice:1, Ben:1, Dave:1
以及
date = Thursday
vclock = Alice:1, Cathy:1
Dave通过决策选择了周四,于是创建了新的消息
date = Thursday
vclock = Alice:1, Ben:1, Cathy:1, Dave:2
所以当最终Alice收集消息时,她收到了来源于Ben的消息
date = Tuesday
vclock = Alice:1, Ben:1, Dave:1
和来源于Cathy的消息
date = Thursday
vclock = Alice:1, Ben:1, Cathy:1, Dave:2
由于存在Dave:1和Dave:2,可以很清楚的判断出Dave的选择,因此Alice得出最终决定,并回复给所有人。Ben知道了大多数的决定,因此也改为同意改日,决策达成。
通过这个例子,可以很明显的看出Vector Clock的工作流程。同时也不难发现其中存在的问题:采用这种方式的话,随着节点的变多,每个节点需要维护的表会变得越来越庞大,使得程序运行效率降低。为了解决该问题。我们可以采用几个服务器记录,而不是记录全部表单,如下图所示。
但是这种做法也不是完美的:对于原本应存在冲突的数据,这里会变得没有冲突,从而直接合并,使得很多消息被直接忽略了。因此,更佳的方法是设定表上限,当超过上限的时候,对最旧的数据进行丢弃。如DHT中的routing table即采取了此方式解决。
参考文献
【1】https://riak.com/posts/technical/why-vector-clocks-are-hard/
【2】https://en.wikipedia.org/wiki/Vector_clock