likes
comments
collection
share

不可靠的时钟

作者站长头像
站长
· 阅读数 16

不可靠的时钟

还在用传统的System.currentTimeMillis来计算代码运行时间嘛? - 掘金 (juejin.cn) 上一篇文章简单地讲到了墙上时钟单调时钟,本篇将从分布式架构的角度下,谈谈为什么我们称时钟为不可靠的时钟

时钟同步与准确性

分布式架构下,每个节点的时间很难保持一致,

石英晶体振荡器

机器的时钟是依靠硬件 石英晶体振荡器 来实现的然而石英振荡器会受到温度的影响。

虚拟化切换

即便时在 虚拟机环境 下,CPU核会切换虚拟机,从而导致正在运行的操作系统会突然短暂暂停几十毫秒。

那为什么我们现在的电脑时钟并没有差很多呢。

NTP(network time protocol)

因为我们使用 NTP 进行同步时钟。NTP 提供了时间校准的功能,可以让机器上的时钟相对来说误差更小,但是没办法实现全部一致。

其次这里会同步是需要走 网络 的,然而网络跟时钟都是 不可靠的 因素,比如 网路阻塞进程暂停 导致超时等。

影响时钟的还有自然因素。

闰秒

正常情况下,一分钟是60秒。但由于地球的自转不均衡和潮汐的问题(具体感兴趣可以自己百度),可能会出现一分钟只有59秒或是61秒。

依赖同步的时钟

网络故障很容易被发现,但是时钟同步出现故障一般都是潜在故障,难以发现。

网络故障,会出现大量的超时请求。这个是可以轻易地被监控到的。当应用程序监控到了之后,可以采取相对应的fail-over措施。

  • 节点下线处理。
  • leader选取等。

但是对于时钟故障就很难立刻让应用程序反应出来。

时钟不一致导致的故障

比如:时间往后跳了1秒、2秒对日常功能可能不会引起大问题。但是对于依赖时钟的服务,比如:

  • 限时购买
  • 定时推送
  • 租约(分布式锁)
  • ....

出现的问题可能就是:

  • 虽然已经到点了,我这个节点上的请求还可以继续购买
  • 现实世界中,我是抢到了第一,但是在程序里,因为我节点的时间比别人慢,可能别人就是第一了。(事件顺序性

不可靠的时钟

  • 客户端A写入x = 1成功后,开始进行节点数据同步。
  • 客户端B写入x = x + 1成功后,开始进行节点数据同步。虽然客户端B是在A之后写入的,虽然客户端A同步开始得比客户端B要早,但是最后客户端B的请求同步比客户端A的要早

先出发的未必先到达

那么在节点2的视角来看,由于时间戳,会判断客户端B的请求先于客户端的请求,他的视角来看是,x+=1 -> x = 1。这样就会导致客户端B的请求被覆盖了。

这种解决冲突的方式就是LWW最后写入获胜 Last-Writer-Win)。这个策略是依赖写入时间,也就是依赖时钟的。这是他无法解决的缺点。

版本号技术

时钟(时间戳)大量用于版本号的生成。

其次对于 事件发生的因果顺序 ,一般我们都会采取 版本号 的技术,因为 自然数是一个全序关系 可以进行比较。这样我们就可以判断事件A是发生在事件B之前还是之后。

对于 版本号 的生成方式有很多选型:

  • 如果采用 wall-clock time墙上时钟) 生成的时间戳,在单机情况下是没有问题的,但是在多节点的情况下,极小概率会出现问题,有可能现实世界中发生的顺序,在程序中是不一样的。
  • 如果采用 monotonous time单调时钟),就可以解决墙上时钟的问题,但是又引入了另外一个问题,这个单调时钟该怎么生成,由主节点生成吗?那么他会有单点故障的风险吗?

所以说在讨论一项技术的时候,我们需要了解他的 优势 以及 代价。再根据我们的场景进行取舍。

总结

分布式集群情况下,各个节点难以保持同样的时钟,影响时钟不可靠的因素有许多:

  • 硬件本身的问题(石英晶体受问题影响,时钟飘逸、时钟回拨等问题)
  • 虚拟化切换的问题
  • 不可靠的网络(NTP同步受到网络的限制)
  • ...

常见的 版本号 技术采用时间戳来进行实现,同样会面临 不可靠的时钟 问题,在跨节点的环境下,过分依赖时钟是一个比较危险的操作。

全局快照的同步时钟 目前业内都没有一个很好的落地方案。

Google Spanner 采用的是一个 置信区间 的机制来实现高精度的时钟(尽可能缩小误差区间),同样他 Google 还部署了高精度的时钟仪器比如 GPS接收器或者原子钟 尽可能地减少误差,感兴趣可以了解一下。

不可靠的时钟

来都来了,点个赞再走吧彦祖👍,这对我来说非常重要!

转载自:https://juejin.cn/post/7249981013416984631
评论
请登录