在分布式系统中,多个节点协同工作。客户端请求会发送不同的节点上处理,这些请求就成为了一个一个的节点上的事件。我们将看到,系统的状态由这些一个一个的事件按照特定的顺序执行之后形成。因此,事件的顺序尤为重要。不同的节点中,看到的事件先后顺序可能会有差别,这也造成了这些节点可能形成不同的状态。
由此可见,处理多个事件时,事件的先后顺序将影响节点的状态,因此如何测量事件的顺序就成了分布式系统中的一个核心问题。一个自然的想法是,按照事件发生的物理时间排序即可。我们将在后续章节了解物理时钟原理,从而遗憾地发现:在一个分布式系统中,通过对比多个节点之间的物理时间来判断事件发生的先后顺序并不精确,甚至有时候会发生错误,而一旦事件的顺序错乱,整个分布式集群的状态也就错乱了。
如果物理时间不可行,那么还有什么办法决定一个分布式系统中事件的先后顺序?答案是逻辑时钟。
令人惊讶的是,前面我们一再谈及事件的先后顺序尤为重要,但是在分布式系统中,还可能出现两个事件无法判断其发生先后顺序的情况,这样的事件称为*“并发事件”*。为了解释事件的顺序,我们还需要了解两个数学中的定义:偏序和全序。事实上,这两个数学定义应该深植在每个分布式系统工程师的心里,在后面我们还会一再见到这两个定义在分布式系统中的应用。
我们将在本章中讨论以下的主题:
- 事件发生的先后顺序是如何影响系统的状态的;
- 物理时间为何不能在分布式系统中用来衡量事件的先后顺序;
- 逻辑时钟的理论基础和计算规则;
- 偏序和全序的直观解释和定义;
- 基于逻辑时钟延伸出来的向量时钟。
状态、事件和快照 #
在一些刑侦片里,经常看到这样的桥段:警方跟踪的一个嫌疑人,在某一天突然发生了一些变化,于是警方就会调取记录,来查看过去这一天里,嫌疑人去过的地方、见过的人、做过的事情,以了解到底是什么原因导致了嫌疑人的变化。
在这里,“去过的地方、见过的人、做过的事情"就是一个一个的事件,如果把嫌疑人看成一个系统,昨天的嫌疑人,和经历了前面这些事件的嫌疑人,就是这个系统在不同时间的状态。(由此我们不妨思考一个哲学问题,如图1所示:过去的"我”,在经历一系列事件之后,才成为现在的"我",这两个"我"是同一个"我"吗?)

我们用一个简单的存储服务来继续解释状态和事件。如图2所示的一个存储服务,最初的状态为$\{x = 1\}$,在顺序执行了命令$set \ x = 2$和$set \ x = 3$之后,新的状态变成了$\{x = 3\}$。在这里,这两个命令的执行顺序尤为重要,如果调换这两个事件的执行顺序就变成了另一个状态$\{x = 2\}$。

从以上的例子中我们也可以看到,在这里并没有提及事件执行的物理时间,而是更关注事件执行的顺序。之所以更关注事件之间的相对顺序而不是物理时间,是因为在分布式系统中,并不存在全局统一的物理时间,我们将在后续章节继续深入讨论物理时间。如果把系统看做一个大的状态机,事件就是改变这个状态机状态的操作。在一个状态机中,只要保证每一次能够按照同样的顺序来执行事件,就能保证这个系统总是能到达同样的状态。
按照相同顺序执行同样的事件就能得到相同的结果,这也是*状态机复制(state machine replication)*的核心思想:
如果两个不同的进程,以同样的初始状态开始运行,按照相同的顺序来处理输入的数据,将得到同样的输出结果。

备注:我们在讨论了逻辑时钟以后,可以看到事件的执行顺序就是某种意义上的逻辑时间。
另一方面,在不同时间节点上该存储的瞬时状态,就被称为在这个时间点的快照(snapshot)。例如,在车水马龙的街道上,拍下一张照片,就是这个街道在那一瞬时时间点的快照。做为对比,事件是用于改变系统状态的动态数据,而快照就是系统状态的静态数据。
到了这里,我们可以给出状态、事件和快照的直观解释:
- 状态(status):一个系统中所有数据的值,状态会随着事件发生改变;
- 事件(event):能够改变系统状态的操作。在一个服务中,所有写请求(写入、更新、删除等),都可以称为事件;
- 快照(snapshot):快照是一种特殊的状态,快照是静止的状态,一旦确定了快照的时间,也就确定了快照的内容。
如下图所示,如果分别在初始时、执行命令$set \ x = 2$之后和执行命令$set \ x = 3$之后,获取系统的快照数据,则会分别得到$\{x = 1\}$、$\{x = 2\}$和$\{x = 3\}$。

从以上对状态、事件、快照的讲解可以看到,这几个概念都与时间强相关,尤其是事件与时间顺序相关。我们接下来看,如何在分布式系统中衡量在不同节点上事件的先后顺序。
备注:将保存系统的相关数据划分为状态、事件、快照这三类数据,也是分布式系统中进行数据复制时的做法,我们在这里强调了在分布式系统中,事件要在多个副本之间保持同样的顺序,比这个条件更强的要求是确定性,我们将在讨论副本间数据复制时继续讨论这个话题。我们还将在后续章节中看到,共识算法本质上就是在多个副本之间维护同样顺序的日志系统。
物理时钟 #
物理时间源和表示法 #
如今的大多数计算机时钟采用的是石英钟(Quartz clocks)。石英钟通常由电池供电,核心是一个石英晶体(通常是音叉形状)。当对石英晶体施加电压时,它会因压电效应产生机械振动,频率非常稳定(通常为32,768 Hz)。这种高精度振荡是石英钟计时的基础。石英钟每振荡H次,计算机内的定时器芯片就中断一次(被称为时钟滴答(clock tick))。中断处理程序会递增一个计数器,该计数器会记录从过去(纪元)开始的刻度数。知道了每秒的刻度数,我们就可以计算出年、月、日和时间等。