分布式系统的时间

2023-11-09

分布式系统的时间

事件的顺序

大家都知道,Linearizability在一些系统(譬如分布式数据库)里面是非常重要的,我们不能允许数据更新之后仍然能读到原先的值,譬如银行转账,用户A有100元,转给用户B 10元,这个操作之后用户A只可能有90元了,但如果A后续发起了另一个转账请求给C转10元的时候,事务里面读到A的余额仍然是100元,这个问题就大了。

一个更简化的说明,假设在某个时间点t我们写入了一条数据,譬如set a = 10,那么在t之后,所有的read a读到的都应该是10,而不是a以前的值。

那么,我们如何确定read a读到的一定是最新的数据呢?在一个系统里面,如果一个事件a发生在另一个事件b的前面,我们就可以认为a happended before b, 可以用 a -> b来表示。

如果a和b是在同一个进程里面执行,(注:这里我们假定进程是顺序执行event的),那么我们可以很容易的就知道a在b之前发生,因为a在进程里面是先执行的。

但在分布式系统里面,a和b可能在不同的进程里面(当然这两个进程一定会相互通讯),这件事情就不是特别容易判断了,我们得找到一个基准用来衡量到底谁先谁后。

我们首先就想到的是时间,我们只要知道a和b发生的时间就能够知道先后顺序了,但是我们都知道,每台机器的时间都不是一致的,虽然能通过NTP协议进行相互校对,但总有误差。所以我们不能直接使用系统时间来判断事件的先后顺序。

Logic Clock

Lamport(这个大牛就不用多介绍了)在上世纪70年代,就提出了使用Logic Clock来确定事件的先后顺序。

我们可以认为Logic Clock就是一个不断增长的number,假设两个进程P1和P2,两个事件a和b,我们用C(a),C(b)来表示这两个事件的logic clock。

如果a和b都是在P1里面发生,如果a -> b,那么我们一定可以知道 C(a) < C(b)。

现在复杂的是a在P1里面发生,但b在P2里面发生,首先我们需要明确P1和P2一定能够相互通讯,a发生之后,P1会给P2发送相关消息,同时会带上C(a),P2接受到这个消息之后再处理b,因为P2明确知道这个消息是从P1发过来的,如果P2当前的clock为C(old)并且小于或者等于C(a),那么会将自己的clock更新为大于C(a)的任意值,不过通常就是C(a) + 1了,这时候在执行b,我们就一定能知道C(b) > C(a)了。

当然,上面a和b必须是相关的事件,也就是a -> b,如果a和b是独立的,那么P1和P2就不需要进行相关的交互了。

可以看到,Logic Vector的原理是非常简单的,但是它因为没有实际的物理时间概念,所以如果我们想根据某一个真实的时间来查询相关事件,这个办不到了。

在Logic Clock之后,人们又引入了Vector Clock,但vector clock也有logic clock同样的问题,不能依据真实的时间来查询,这里就不多介绍vector clock了。

True Time

前面我们说了,NTP是有误差的,而且NTP还可能出现时间回退的情况,所以我们不能直接依赖NTP来确定一个事件发生的时间。在Google Spanner里面,通过引入True Time来解决了分布式时间问题。

Spanner通过使用GPS + Atomic Clock来对集群的机器进行校时,精度误差范围能控制在ms级别,通过提供一套TrueTime API给外面使用。

TrueTime API很简单,只有三个函数:

Method	        Return
TT.now()	TTinterval: [earliest, latest]
TT.after(t)	true if t has definitely passed
TT.before(t)	true if t has definitely not arrived

首先now得到当前的一个时间区间,spanner不能得到精确的一个时间点,只能得到一段区间,但这个区间误差范围很小,也就是ms级别,我们用ε来表示,也就是[t - ε, t + ε]这个范围,

假设事件a发生绝对时间为tt.a,那么我们只能知道tt.a.earliest <= tt.a <= tt.a.latest, 所以对于另一个事件b,只要tt.b.earliest > tt.a.latest,我们就能确定b一定是在a之后发生的,也就是说,我们需要等待大概2ε的事件才能去提交b,这个就是spanner里面说的commit wait time。

可以看到,虽然spanner引入了TrueTime可以得到全球范围的时序一致性,但相关事务在提交的时候会有一个wait时间,只是这个时间很短,而且spanner后续都准备将其优化到 ε < 1ms,也就是对于关联事务,仅仅在上一个事务commit之后等待2ms之后就能执行,性能还是很强悍的。

但spanner有一个最大的问题,TrueTime是基于硬件的,而现在对于很多企业来说,是没有办法搞定这套部署的。所以如果Google能将TrueTime的硬件设计开源,那我觉得更加造福社区了。

Hybrid Logic Clock

既然TrueTime这种硬件方案很多人搞不定,那么我们就采用软件方案了。

Cockroachdb使用了Hybrid Logic Clock(HLC)来解决分布式时间的问题。

HLC是基于NTP的,但它只会读取当前系统时间,而不会去修改,同时HLC又能保证在NTP出现同步问题的时候仍能够很好的进行容错处理。对于一个HLC的时间t来时,它总是大于等于当前的系统时间,并且与其在一个很小的误差范围里面,也就是 |l - pt| < ε。

HLC由两部分组成,physical clock + logic clock,l.j维护的是节点j当前已知的最大的物理时间,c.j则是当前的逻辑时间。那么判断两个事件的先后顺序就很容易了,先判断物理时间pt,在判断逻辑时间ct。

HLC的算法如下,在节点j上面:

  • 初始化: l.j = 0, c.j = 0
  • 给另一个进程发送或者处理自己的事件:

  l'.j = l.j;
  // 跟当前系统时间比较,得到pt
  l.j = max(l'j, pt.j)
  // 如果pt没有变化,则c.j加1,如果有变化,因为这时候
  // 铁定PT变大了,所以我们可以将ct清零
  if (l.j = l'.j) {
      c.j = c.j + 1
  } else {
      c.j = 0
  }

  // Timestamp with l.j, c.j

  • 接受某一个节点m的消息事件:

  l'.j = l.j;
  // 跟当前系统事件以及节点m的pt比较,得到pt
  l.j = max(l'.j, l.m, pt.j)
  if (l.j = l'.j = l.m) {
      // pt一样,获取最大的ct,并加1
      c.j = max(c.j, c.m) + 1
  } else if (l.j = l'j) {
      // 这里表明j原来的pt比m大,只需要增加ct
     c.j = c.j + 1
  } else if (l.j = l.m) {
      // 这里表明m的pt比j原来的要大,所以直接可以用m的ct + 1
      c.j = c.m + 1
  } else {
      // pt变化了,ct清零
      c.j = 0
  }

  // Timestamp with l.j, c.j

具体的实现算法,可以看cockroachdb的HLC实现

HLC虽然方便,它毕竟是基于NTP的,所以如果NTP出现了问题,可能导致HLC与当前系统pt的时间误差过大,其实已经不怎么精确了,HLC论文提到对于一些out of bounds的message可以直接忽略,然后加个log让人工后续处理,而cockroachdb是直接打印了一个warning log。

Timestamp Oracle

无论上面的Ture Time还是Hybrid Logic Time,都是为了在分布式情况下获取全局唯一时间,如果我们整个系统不复杂,而且没有spanner那种跨全球的需求,有时候一台中心授时服务没准就可以了。

在Google Percolator系统这,他们就提到使用了一个timestamp oracle(TSO)的服务来提供统一的授时服务,为啥叫oracle,我猜想可能底层用的就是oracle数据库。。。

使用TSO的好处在于因为只有一个中心授时,所以我们一定能确定所有时间的时间,但TSO需要关注几个问题:

  • 网络延时,因为所有的事件都需要从TSO获取时间,所以TSO的场景通常都是小集群,不能是那种全球级别的数据库。
  • 性能,TSO是一个非常高频的操作,但鉴于它只干一件事情,就是授时,通常一个TSO每秒都能支持10w+以上的QPS,而这个对很多应用来说是绰绰有余的。
  • 容错,TSO是一个单点,所以不得不考虑容错,而这个现在基于zookeeper,etcd也不是特别困难的事情。

所以,如果我们没法实现TrueTime,同时又觉得HLC太复杂,但又想获取全局时间,TSO没准是一个很好的选择,因为它足够简单高效。

我们现在的数据库产品就使用的是TSO方案,但也不排除以后为了支持全球同步,而考虑使用TureTime或者HLC的方案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分布式系统的时间 的相关文章

  • 分布式系统SDK端重试策略

    分布式系统SDK端重试策略 1 API 的属性 成功率优先 强调成功率 所以重试的时候 sleep 时间较长 按照指数退避的方式sleep latency优先 强调latency 所以重试的时候 sleep的时间较短 2 重试次数 retr
  • 高效实现延迟消息功能

    高效实现延迟消息功能 高效延时消息 包含两个重要的数据结构 1 环形队列 例如可以创建一个包含3600个slot的环形队列 本质是个数组 2 任务集合 环上每一个slot是一个Set 同时 启动一个timer 这个timer每隔1s 在上述
  • Serverless架构模式简介

    Serverless架构模式简介 一 简介 Serverless是一种无服务的架构 类似aws lambda Serverless与跟传统架构不同 由开发者实现的服务端逻辑运行在无状态的计算容器中 它是由事件触发 短暂的 可能只存在于一次请
  • 飞天平台安全相关

    飞天平台安全相关 1 capability机制 用户的身份认证 authentication 是基于密钥机制的 用户对资源的访问控制是基于权能 capability 机制进行授权 authorization 的 capability是用于访
  • 阿里云飞天系统

    阿里云飞天系统 有幸在阿里云飞天部门工作几年 下面给出基础架构一览
  • 分布式系统之数据分片

    分布式系统之数据分片 详细参考 http www cnblogs com xybaby p 7076731 html
  • Quorum Journal Manager原理

    Quorum Journal Manager原理 在一个典型的HA集群 两个独立的物理节点配置为NameNodes 在任何时间点 其中之一NameNodes是处于Active状态 另一种是在Standby状态 Active NameNode
  • 分布式配置管理系统QConf

    分布式配置管理系统QConf 分布式配置管理系统QConf是360公司开源的系统 详见 https github com Qihoo360 QConf 整体架构图如下 资料 1 https github com Qihoo360 QConf
  • 分布式系统的正确性验证方法

    分布式系统的正确性验证方法 1 Jepsen框架 Jepsen是一个开源的分布式一致性验证框架 可用于验证分布式数据库 分布式消息队列 分布式协调系统 Jepsen探索特定故障模式下分布式系统是否满足一致性 Jepsen框架是一个
  • 一种多级缓存的系统架构

    一种多级缓存的系统架构 下面这个也是比较常用的多级缓存的系统架构图 整体流程如上图所示 1 首先接入Nginx将请求负载均衡到应用Nginx 此处常用的负载均衡算法是轮询或者一致性哈希 轮询可以使服务器的请求更加均衡 而一致性哈希可以提升应
  • Tachyon内存文件系统

    Tachyon内存文件系统 Tachyon是以内存为中心的分布式文件系统 拥有高性能和容错能力 能够为集群框架 如Spark MapReduce 提供可靠的内存级速度的文件共享服务 从软件栈的层次来看 Tachyon是位于现有大数据计算框架
  • Nginx+Redis+Ehcache:大型高并发与高可用的三层缓存架构总结

    Nginx Redis Ehcache 大型高并发与高可用的三层缓存架构总结 Nginx 对于中间件nginx常用来做流量的分发 同时nginx本身也有自己的缓存 容量有限 我们可以用来缓存热点数据 让用户的请求直接走缓存并返回 减少流向服
  • 实现一个高性能网络通讯库的要点

    实现一个高性能网络通讯库的要点 由于硬件的发展速度快 本来占时间消耗小头的软件层 变成了大头 原本占性能比例非常小的的中断 上下文切换 也成为了性能优化的方向 许多bypass kernel的方案开始发展起来 以前在千兆网卡普及的时代 就有
  • 分布式系统的时间

    分布式系统的时间 事件的顺序 大家都知道 Linearizability在一些系统 譬如分布式数据库 里面是非常重要的 我们不能允许数据更新之后仍然能读到原先的值 譬如银行转账 用户A有100元 转给用户B 10元 这个操作之后用户A只可能
  • API网关

    API网关 api gateway 即 api 网关 所有的请求首先会经过这个网关 这有点类似于前端控制器模式 也有点类似于 Facade模式 如下图所示 由于所有的请求会先经过这个 api 网关 所以 可以在 这里做 权限控制 安全 负载
  • condition update在分布式系统中设计

    condition update在分布式系统中设计 1 定义 condition update称为条件更新 用于分布式系统中数据一致性 能够保证在并发操作数据时的正确性 2 方式 1 可以通过version来保证condition upda
  • 分布式系统全链路监控方案设计

    分布式系统全链路监控方案设计 在分布式系统中 全链路监控系统 跟踪requestid经过了哪些server 方便定位log的位置 能在一定程度上缓解维护压力 下面给出我们团队的架构设计图
  • DNS在架构设计中的巧用

    DNS在架构设计中的巧用 一 缘起 一个http请求从客户端到服务端 整个执行流程是怎么样的呢 一个典型流程如上 1 客户端通过域名daojia com请求dns server 2 dns server返回域名对应的外网ip 1 2 3 4
  • 聊聊java高并发系统之异步非阻塞

    聊聊java高并发系统之异步非阻塞 几种调用方式 同步阻塞调用 即串行调用 响应时间为所有服务的响应时间总和 半异步 异步Future 线程池 异步Future 使用场景 并发请求多服务 总耗时为最长响应时间 提升总响应时间 但是阻塞主请求
  • 大规模分布式消息中间件简介

    大规模分布式消息中间件简介 当前各种 RPC 中间件技术已经广泛应用于各个领域 其中 服务器之间消息通讯这种功能广泛应用于这些中间件中 于是 将这种面向消息的中间件 Message Oriented Middleware MOM 抽象出来

随机推荐

  • Ubisoft Connect失去连接解决办法

    Ubisoft Connect失去连接解决办法 原视频地址 育碧平台失去连接100 解决 哔哩哔哩 bilibili 首先打开服务 有两种方式 win R打开命令窗口输入services msc 打开任务管理器切换到服务选项卡 然后找到Sp
  • 基于Python的开源人脸识别库:离线识别率高达99.38%

    项目地址 https github com ageitgey face recognition face recognition 本文的模型使用了C 工具箱dlib基于深度学习的最新人脸识别方法 基于户外脸部数据测试库Labeled Fac
  • mysql innodb引擎什么时候表锁什么时候行锁?

    mysql innodb引擎什么时候表锁什么时候行锁 InnoDB基于索引的行锁 InnoDB行锁是通过索引上的索引项来实现的 这一点 ySQL与Oracle不同 后者是通过在数据中对相应数据行加锁来实现的 InnoDB这种行锁实现特点意味
  • 用Java写一个小游戏

    源码地址 https pan baidu com s 18y8Et8QnahhDdz7N 0Rsg 提取码 b3tr 游戏开始图片 如下 游戏胜利图片 如下 游戏分析 玩家控制键盘上下左右键 当数字按照从小到大依次排列的时候则玩家获胜 游戏
  • MATLAB如何将文本与数字进行线性回归比较?(已经解决)

    2 虽然导入进去了 但是文本是无法和数值进行比较的 所以我采用了一个替换的方式 就是把sex里面的male与female换成数字的 1 和 2 这样再把 1 和 2 换成double型就可以进行回归分析了 因为sex是cell型 是无法与d
  • LeetCode-109.有序链表转换二叉搜树

    二叉搜索树 二叉查找树又称二叉搜索树或者二叉排序树 它可以是一个空树或者是一个二叉树 既有链表的快速插入与删除的特点 又有数组快速查找的优势 具有以下性质 若左子树非空 则左子树所有节点均小于根节点的值 若右子树非空 则右子树所有节点均大于
  • 反转链表(双指针+递归)

    本题出自LeetCode第206题 最普通的方法 无非是找一中间量 用于二者之间的置换 采用双指针 class Solution public ListNode reverseList ListNode head ListNode cur
  • IsBadReadPtr函数和异常处理

    起因是优化代码性能 注意到这个函数 搜了一下发现是微软弃用的函数 说是有线程安全问题 经过一系列操作发现 处理大文件时这个函数会导致耗时变长 于是就研究一下这个函数 首先看函数开头 mov edi edi push ebp mov ebp
  • 右脑记忆法的个人理解

    先写个提纲 右脑记忆法 王峰 袁文魁等的记忆方法基础 也是大脑锦标赛 记忆大师的通用方法学 说是右脑记忆 其实就是图像记忆 因为形象化的信息 更容易记忆 最强大脑节目 记忆是很关键的一项能力 走进科学 记忆有魔方 http tv peopl
  • linux安装南大通用数据库 GBase 8s V8.8

    linux安装南大通用数据库 GBase 8s V8 8 1 操作系统 数据库 2 下载链接 3 安装文档 4 安装前准备 4 1 以root用户创建 gbasedbt 组和用户 4 2 创建 GBase 8s 数据库安装目录 4 3 上传
  • 缠论的基本原理

    缠论的基本原理 飞吻 短期均线略略走平后继续按原来趋势进行下去 14课 唇吻 短期均线靠近长期均线但不跌破或升破 然后按原来趋势继续下去 14课 湿吻 短期均线跌破或升破长期均线甚至出现反复缠绕 如胶似漆 14课 女上位 短期均线在长期均线
  • 【Unity技巧】Unity中的优化技术

    原文地址 写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的 Digital Tutors是一个非常棒的教程网站 包含了多媒体领域很多方面的资料 非常酷 除此之外 还参考了Unity Cookie中的一个教
  • 【论文学习】Fast R-CNN

    论文地址 Fast R CNN 1 Abstract Fast R CNN也是主要应用在目标检测的一种方法 它建立在前人工作的基础上 利用深度卷积网络更加高效地对目标进行分类 与之前的工作相比 Fast R CNN采用了一些在提升检测精度的
  • springBoot的分页插件

    目录 1 加入依赖 2 配置分页插件 3 使用分页插件 1 加入依赖
  • 初识小熊派——小熊派功能简介

    小熊派功能简介 小熊派IoT开发板一款由南京小熊派智能科技有限公司联合华为技术有限公司基于STM32L431RCT6设计的高性能物联网开发板 开发板充分考虑物联网感知层设备的多样性 具有强大的可扩展性 用于提供给开发者评估及快速设计相关物联
  • UDP和TCP的区别

    UDP User Datagram Protocol 和 TCP Transmission Control Protocol 是两种常见的传输层协议 它们在设计和用途上有很大的区别 以下是它们的主要差异 连接性 TCP 是一个连接导向的协议
  • mysql安装教程【安装版】

    一 下载 进入mysql官方网站MySQL MySQL Downloads 下载社区版 MySQL Community GPL Downloads MySQL Installer for Window 第一个 大小是10多M 是联网在线安装
  • IDEA中控制台乱码解决方法

    文章目录 1 在设置中的 文件编码 中将3个位置设为UTF 8 注 此处设置与控制台乱码无关 3处可均设为UTF 8或均设为系统默认值 2 在Tomcat的 编辑配置 中 将VM options设为 Dfile encoding GBK 与
  • git创建空白分支,并将本地文件上传到分支

    git创建空白分支 并将本地文件上传到分支 1 创建一个空白的分支的需求 在Git中创建分支 是必须有一个父节点的 也就是说必须在已有的分支上来创建新的分支 如果工程已经进行了一段时间 这个时候是无法创建空分支的 但是有时候就是需要创建一个
  • 分布式系统的时间

    分布式系统的时间 事件的顺序 大家都知道 Linearizability在一些系统 譬如分布式数据库 里面是非常重要的 我们不能允许数据更新之后仍然能读到原先的值 譬如银行转账 用户A有100元 转给用户B 10元 这个操作之后用户A只可能