0
点赞
收藏
分享

微信扫一扫

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!

TrueTime吗?作为全局时钟的一种实现形式,是Google通过 GPS和原子钟两种方式混合提供的授时机制,误差可以控制在7毫秒以内。正是在这7毫秒内,时光可能倒流。

分布式数据库的很多设计都和时间有关,更确切地说是和全局时钟有关。如线性一致性基础就是全局时钟,MVCC、快照、乐观协议与悲观协议,都和时间有关。

1 常见授时方案

区分授时机制,抓住三要素:

  1. 时间源:单、多个
  2. 使用的时钟类型:物理时钟、混合逻辑时钟
  3. 授时点:一、多个

排列组合8种可能性,NTP(Network Time Protocol)误差大,也不保证单调递增,没单独使用NTP的产品;还有一些方案实践不适用(N/A)。因此常见方案:

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳

1. TrueTime

Spanner采用TrueTime,时间源是GPS和原子钟,属多时间源和物理时钟,同时它也采用了多点授时机制,即集群内有多个时间服务器可提供授时服务。

TrueTime会出现时光倒流。如A、B两个进程先后调用TrueTime服务,各自拿到一个时间区间,在其中随机选择,可能出现B时间早于A的时间。任何物理时钟都存在时钟偏移甚至回拨。

单个物理时钟会产生误差,而多点授时又带来整体性误差,那TrueTime为啥还这么设计?

因为它有两个显著优势:

  • 高可靠高性能,多时间源和多授时点实现完全的去中心化设计,不存在单点
  • 支持全球化部署,客户端与时间服务器的距离可控,不会因两者通讯延迟过长导致时钟失效

2. HLC(Hybrid Logical Clock),混合逻辑时钟

CockroachDB和YugabyteDB也是以高性能高可靠、全球化部署为目标,但Truetime是Google独门绝技,依赖特定硬件设备,不适于开源软件。所以,它们使用HLC,同样是多时间源、多点授时,但时钟采用物理时钟与逻辑时钟混合。HLC实现也复杂,且和TrueTime同样有整体性时间误差。

对此共性问题,Spanner和CockroachDB都通过一些容错设计消除时间误差。

3 TSO(Timestamp Oracle)

其他分布式数据库大多选择单时间源、单点授时的方式,承担这功能的组件:

  • 在NewSQL架构称为TSO
  • 在PGXC架构称为全局事务管理器(Golobal Transcation Manager,GTM)

即一个单点递增的时间戳和全局事务号基本等效。

优点

实现简便,如能保证时钟单调递增,还可简化事务冲突时的设计。

缺点

集群不能大范围部署,性能有上限。

TiDB、OceanBase、GoldenDB和TBase等选择这方向。

4 STP

小众方案,如巨杉的STP(SequoiaDB Time Protoco)。单时间源、多点授时,优缺点介于HLC和TSO。

TrueTime是基于物理设备的外部授时方案,所以Spanner直接使用就可以了,自身不需要做专门的设计。而对于其他3种方案,如想深入理解,还得结合具体产品。

2 中心化授时:TSO(TiDB)

最早提出TSO的,大概是Google论文“ Large-scale Incremental Processing Using Distributed Transactions and Notifications”,主要介绍分布式存储系统Percolator的实现机制,提到通过一台Oracle为集群提供集中授时服务,称为Timestamp Oracle。所以,后来很多分布式系统也用它缩写命名自己的单点授时机制,如TiDB和Yahoo的Omid。

TiDB全局时钟是个数值,两部分构成:

  • 高位是物理时间,即操作系统的毫秒时间
  • 低位是逻辑时间,18位数值。从存储空间看,1ms最多产生262,144个时间戳(2^18)

单点授时先要解决单点故障。TiDB提供授时服务的节点被称Placement Driver。多个PD节点构成一个Raft组,通过共识算法保证主节点宕机后马上选新主,短时内恢复授时服务。

如何保证新主产生的时间戳一定大于旧主呢?

须将旧主的时间戳存储,存储也须高可靠,所以TiDB使用etcd。但每产生一个时间戳都保存吗?时间戳的产生速度直接与磁盘I/O能力相关,存在瓶颈。

TiDB采用预申请时间窗口的方式:

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳_02

当前PD(主节点)系统时间103毫秒,PD向etcd申请一个“可分配的时间窗口”。

时间窗口的跨度可参数指定,系统默认3毫秒,示例采用默认,所以这窗口起点是PD当前时间103,时间窗口终点在106毫秒。

写入etcd(106)成功后,PD得到一个从103到106的“可分配时间窗口”,在这时间窗口内PD可使用系统的物理时间作为高位,拼接自己在内存中累加的逻辑时间,对外分配时间戳。

该设计意味着:

所有已分配时间戳的高位,即物理时间永远<存储的(3)所有PD已分配时间戳的高位,即物理时间永远<etcd存储的max

PD主节点宕机,新主就可读取etcd中存储的最大值,在此基础申请新的“可分配时间窗口”,这样新主分配的时间戳肯定大于旧主。

为降低通讯开销,每个客户端一次可申请多个时间戳,时间戳数量作为参数,由客户端传给PD。一旦在客户端缓存,多个客户端之间时钟就不再是严格单调递增的,这也是追求性能需要付出的代价。

3 分布式授时:HLC(CockroachDB)

TrueTime依赖Google强大工程能力和特殊硬件,不具普适性。HLC作为纯软实现,更灵活,在CockroachDB、YugabyteDB和很多分布式存储系统广泛使用。

HLC不只字面意思, TiDB的TSO也混合物理时钟与逻辑时钟,但截然不同。HLC代表了一种计时机制,首次提出在论文“Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases”中,CockroachDB和YugabyteDB设计灵感都来自这论文。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳_03

假如有ABCD四节点,方框是节点上发生的事件,方框内三数依次是节点的本地物理时间(简称本地时间,Pt)、HLC的高位(L值)和HLC的低位(C值)。

A节点的本地时间初始值为10,其他节点的本地时间初始值都是0。四个节点的第一个事件都是在节点刚启动的一刻发生的。先看A1,HLC(10,0),其中高位直接取本地时间,低位从0开始。同理,其他事件的HLC都是(0,0)。

随时间推移,接下来事件如何计时。

事件D2发生时,首先取上一个事件D1的L值和本地时间比较。L值等于0,本地时间已经递增变为1,取最大值,那么用本地时间作为D2的L值。高位变更了,低位要归零,所以D2的HLC就是(1,0)。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳_04

D1一样,只不过D1没有上一个事件的L值,只能用0代替,特殊情况。

如节点间有调用关系,计时逻辑会更复杂一点。看事件B2,要先判断B2的L值,就有三个备选:

  1. 本节点上前一个事件B1的L值
  2. 当前本地时间
  3. 调用事件A1的L值,A1的HLC是随着函数调用传给B节点的

这三个值分别是0、1和10。按照规则取最大值,所以B2的L值是10,也就是A1的L值,而C值就在A1的C值上加1,最终B2的HLC就是(10,1)。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_分布式数据库_05

B3事件发生时,发现当前本地时间比B2的L值还要小,所以沿用了B2的L值,而C值是在B2的C值上加一,最终B3的HLC就是(10,2)。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_分布式数据库_06

论文中用伪码表述了完整的计时逻辑,我把它们复制在下面,你可以仔细研究。

1Initially l:j := 0; c:j := 02Send or local event3l’:j := l:j;4l:j := max(l’:j; pt:j);5If (l:j=l’:j) then c:j := c:j + 16Else c:j := 0;7Timestamp with l:j; c:j8Receive event of message m9l’:j := l:j;10l:j := max(l’:j; l:m; pt:j);11If (l:j=l’:j=l:m) then c:j := max(c:j; c:m)+112Elseif (l:j=l’:j) then c:j := c:j + 113Elseif (l:j=l:m) then c:j := c:m + 114Else c:j := 015Timestamp with l:j; c:j

其中,对于节点J,l.j表示L值,c.j表示C值,pt.j表示本地物理时间。

在HLC机制下,每个节点会使用本地时钟作为参照,但不受到时钟回拨的影响,可以保证单调递增。本质上,HLC还是Lamport逻辑时钟的变体,所以对于不同节点上没有调用关系的两个事件,是无法精确判断先后关系的。比如,上面例子中的C2和D2有同样的HLC,但从上帝视角看,C2是早于D2发生的,因为两个节点的本地时钟有差异,就没有体现这种先后关系。HLC是一种松耦合的设计,所以不会去校正节点的本地时钟,本地时钟是否准确,还要靠NTP或类似的协议来保证。

4 多层级中心化授时:STP(巨杉)

巨杉采用了单时间源、多点授时机制,它有自己的全局时间协议,称为STP(Serial Time Protocol),是内部逻辑时间同步的协议,并不依赖于NTP协议。

STP体系下各角色节点的关系。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳_07

STP是独立于分布式数据库的授时方案,该体系下的各角色节点与巨杉的其他角色节点共用机器,但没有必然的联系。

STP下的所有角色统称为STP Node,具体分为两类:

  1. STP Server。多个STP Server构成STP Server组,组内根据协议进行选主,主节点被称为Primary,对外提供服务。
  2. STP Client。按照固定的时间间隔,从Primary Server 同步时间。

巨杉数据库的其他角色节点,如编目节点(CATALOG)、协调节点(COORD)和数据节点(DATA)等,都从本地的STP Node节点获得时间。

STP与 TSO一样都是单时间源,但通过增加更多的授时点,避免了单点性能瓶颈,而负副作用是多点授时就会造成全局性的时间误差,因此和HLC一样需要做针对性设计。

5 总结

  1. 分布式数据库有多种授时机制,它们的区别主要看三个维度。一,是单时间源还是多时间源;二,时间源采用的是物理时钟还是混合逻辑时钟;三,授时点是一个还是多个。
  2. TrueTime是多时间源、多授时点方案,虽然仍存在时间误差的问题,但实现了高可靠高性能,能够支持Spanner做到全球化部署,是一种非常强悍的设计方案。TrueTime是GPS加原子钟的整合方案,可以看作为一种物理时钟,它完全独立于Spanner的授时服务,不需要Spanner做专门的设计。
  3. HLC同样是多时间源、多授时点,由于是纯软方案,所以具有更好的通用性。CockroachDB和YugabyteDB都采用了这种方案,也都具备全球化部署能力。HLC的设计基础是Lamport逻辑时钟,对NTP的时间偏移有一定的依赖。
  4. TSO是典型的单时间源、单点授时方案,实现简便,所以成为多数分布式数据库的选择。如果TSO能够做到单调递增,会简化读写冲突时候的处理过程,但缺点是集群部署范围受到极大的限制。
  5. 还有一些小众的方案,比如巨杉的STP,也试图在寻求新的平衡点。

时间误差普遍存在,只不过长期在单体应用系统下开发,思维惯性让我们忽略,随分布式架构的普及,更多的架构设计中都要考虑这因素。

分布式系统的物理时钟与逻辑时钟孰优孰劣?这份抉择指南请收下!_时间戳_08

6 FAQ

时间对于分布式数据库的影响是什么?或者你也可以谈谈在其他分布式系统中曾经遇到的关于时间的问题。

Daniel Peng and Frank Dabek: Large-scale Incremental Processing Using Distributed Transactions and Notifications Sandeep S. Kulkarni et al.: Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases

我理解多授时点应该是指当前集群有多个可以获取时间的服务器。TiDB的PD是通过集群化来做到高可用的,为啥还被归于单授时点?

TiDB的PD虽是高可靠,但工作的只是主节点,所以是单点授时。

多时间源咋理解?Spanner是GPS+物理时钟,是最终时间计算会通过这2个指标计算的意思吗?如果是单时间源的话,获取的时间只取决于一个因素?

多时间源,是说多个独立提供时间的实例,如部分原子钟和GPS坏掉,其他原子钟可照常提供时间不受影响。

工作中分布式数据库落地少。目前项目各种分布式服务,一般都依赖一个雪花算法序列号生成器。为保证序列号的唯一或进一步保证递增,依赖时钟同步。现在一般结合业务场景,对时钟进行一定校验,同时对于时钟回拨做一些容错解决问题。 目前主流还是应用层方案解决分布式各种问题,如果将来分布式数据库成熟了,应用解决方案会大大简化。

hlc判断大小是先高位,再低位,判断时本地时间可忽略?

还是比较本地时间的,否则就是纯粹的逻辑时钟,那样无关事件的时钟偏差就太大。

“103~106 的“可分配时间窗口”,在这时间窗口内 PD 可使用系统的物理时间作为高位,拼接自己在内存中累加的逻辑时间,对外分配时间戳。 ” 如果主PD在104故障,切换新主PD后岂不是105和106的时间窗口都不可用?

是的,已分配的时间窗口就舍弃,保证时间戳不会重叠。

Spanner解决True Time回拨问题,应该使用等待~ True Time会返回一个时间区间,保证真实时间是在这个区间内的,那么Spanner会等待这个时间过去,以此保证时钟不会回拨。

时间决定了数据库系统看到的事件发生顺序。对于对同一条记录进行操作的oplog在不同节点之间复制,然后在不同节点apply的时候,决定了谁在谁之前操作?

有点小问题,复制的顺序并不影响操作的顺序和数据的可见性,还是要看操作的全局时间。

举报

相关推荐

0 条评论