建议先看论文原文再来看这篇文章,我不会对论文中的各个点都具体说明。仅仅是写一些我自己的想法,帮助理解。

大家都知道。分布式环境下。确定各个事件发生的顺序非常重要,不然就会发生一些麻烦的问题。

考虑一下这个问题:小明要用同学给的优惠码在京东上买一本书。如果京东的后台架构如图一所看到的。A是前端代理server,负责接收用户请求,B是优惠码验证server,当用户请求使用优惠码的时候,A会把请求发给B。让B来验证。C是日志server。专门存放日志。系统中的不论什么一个操作都会记录到日志server中。非常显然,这是一个分布式系统。

图一  后台架构

小明的买书过程分为3个步骤:

  1. 请求到达A。A发送一条消息给C,让C记录一条日志[小明下了订单XXX号]
  2. A是异步工作的。发送完1后,A发现小明使用了优惠码,就把优惠码发送给B进行验证
  3. B验证了优惠码发现是合法的。就发送消息给C,使之记录日志[XXX号订单使用了优惠码YYY]

至此,买书过程结束。假设一切正常的话,那日志server上会依次产生[小明下了订单XXX号],[XXX号订单使用了优惠码YYY]这两条日志。

但事情没有这么简单,假设A跟C之间的网络比較慢,导致3在1之前到达C,那么C上就会产生顺序相反的两条日志。这样就出问题了。由于是1的发生才导致了3的发生,这两者能够看成是有因果关系的,可是生成的日志却是相反的。

有什么办法能让系统识别事件的因果关系呢?比較easy想到的是让server每次发送消息的时候带上时间戳,C收到消息之后比对时间戳。就知道谁先谁后了。

问题是,在分布式环境下。各个机器的时钟是无法100%同步的,所以这样的方法不靠谱。

那有没有办法在不借助物理时钟的情况下给分布式环境下的全部事件排序呢?

这就须要借助Lamport大神在1978年提出的Logical Clock。

Happend-before关系

Lamport在文章中首先定义了一种关系,叫做Happend-before,我个人的理解是相当于因果关系,用->表示,比方a->b就叫做a Happend-before b。即b是由a引起的。

若两个事件a,b满足以下任一条件,则记作a->b:

  1. 假设a b是在同一进程内的两个事件。而且a发生在b之前,那么a->b
  2. 假设a代表了某个进程的消息发送事件,b代表还有一进程中针对这同一个消息的接收事件,那么a->b

以下这张图非常好地展示了Happend-before这一关系。

图二  Happend-before关系

P和Q是两个独立的进程,也能够理解为分布式系统中的两台server;时间从上往下递增。圆圈表示事件。

图中红色的箭头就是上述的Happend-before关系。a,b和e,f各自是同一进程中的先后两个事件,满足条件1,b和f各自是消息的发送事件和接受事件。满足条件2。那么事件a和e。b和e。各自是什么关系呢?事实上他们之间没有因果关系。能够觉得他们是并发的。正由于他们没有因果关系,所以从系统的角度来看,他们谁发生在前谁发生在后不重要,仅仅要能辨别清楚有因果关系的事件。使得因果关系不会倒转,那么整个分布式系统就可觉得是正确的,至少从逻辑上不会非常出错。至于那些没有因果关系的并发事件,不用关注他们的先后顺序。(理解这段话是非常重要的,这正是Logical
Clock的精髓所在。

可以这么理解。Logical Clock解决的问题是找到一种方法,给分布式系统中全部时间定一个序,这个序可以正确地排列出具有因果关系的事件(注意,是不能保证并发事件的真实顺序的),使得分布式系统在逻辑上不会发生因果倒置的错误。

那么怎样利用Happend-before关系来定义顺序呢?继续往下看。

Logical Clock

如今我们给系统中全部的事件打上一个时间戳(事实上就是一个递增的序号)。每一个进程维护一个自己的时间戳。时间戳的添加遵循以下两点规则:

  1. 假设两个事件发生在同一个进程上。而且不是接受消息的事件。那么后面事件的时间戳为前面的+1
  2. 假设一个事件是接受消息。那么他的时间戳为本进程前续事件的时间戳与接受到的消息的时间戳中较大者+1

图三  时间戳演示

上图展示了打完时间戳后的样子,可见这个时间戳确保了在因果条件的事件中是递增的。可是并发的事件(如a和e),他们的时间戳是没有可比性的。谁大谁小说明不了问题。记C(a)为事件a的时间戳,那么:

  1. 若a->b,即a与b有因果关系,那么C(a)>C(b)
  2. 若a与b没有因果关系,那么C(a)与C(b)可能是随意关系(大于 小于 等于)

也就是说,依据这个时间戳。是没法反过来判断事件发生的真实顺序的,由于对于并发事件来说。尽管C(a)>C(b),但或许a与b的真实顺序是t(a) < t(b)。

那么C(a)>C(b)就没有意义了?换个角度想想,既然上面说过,并发事件的顺序不重要。不会影响系统的正确性。那么我们任意定一个顺序不就完了吗?就当作时间戳大的事件肯定是发生在后面,时间戳小的事件肯定是发生在前面,这样一来不就统一了因果事件和并发事件的排序了吗?可是另一个问题,两个并发事件的时间戳一样怎么办?那就再加一层假定,给分布式系统中的server编号。当两个时间戳一样时,编号小的server就当他发生在先.

总结一下上面说的,我们能够定义这样一个全序关系”=>”:如果a是进程Pi中事件。b是进程Pj中的事件。那么当且仅当满足例如以下条件之中的一个时:(1)Ci(a)<Cj(b);(2)Ci(a)=Cj(b)且Pi<Pj,那么我们就觉得“a=>b”。

这样一来,我们就完毕了对分布式系统中全部事件的定义。可是大家可能会疑惑,这样是不是有点太任意了。非常不靠谱的感觉。

确实,Lamport也承认这一点,他在论文最后也说了:

The total ordering defined by the algorithm is somewhat arbitrary. It can produce anomalous behavior if it disagrees with the ordering perceived by the system’s users.

可是至少这种方法给出了一个定义分布式系统中事件顺序的方法,他确保的是全部因果关系的事件不会发生逻辑错误,但他并不保证系统的公平性(比方两台server同一时候并发地请求一个资源,物理时间上先发出请求的进程不一定会先得到这个资源。但这顶多会造成不公平,不会造成错误)。

怎么理解呢?看一下以下两个样例。

Logical Clock应用

如今让我们回过头看看文章开头提到的问题。怎么利用Logical Clock保证日志的顺序正确呢?Lamport在论文的后半部分提出了一个算法,能够解决问题。

可是这个算法基于一个前提:对于随意的两个进程Pi和Pj,它们之间传递的消息是依照发送顺序被接收到的。这个如果并只是分,TCP就能够满足要求。

  1. 首先。每一个进程会维护各自在本地维护一个请求队列。

    算法是由例如以下5个规则定义的。

    方便起见,每条规则定义的行为会被做为一个独立事件。

  2. 为请求该项资源(在这个问题中,资源就是日志server),进程Pi发送一个(Tm,Pi)资源请求消息给其它全部进程,并将该消息放入自己的请求队列,在这里Tm代表了消息的时间戳
  3. 当进程Pj收到(Tm,Pi)资源请求消息后,将它放到自己的请求队列中。并发送一个带时间戳的确认消息给Pi。(注:假设Pj已经发送了一个时间戳大于Tm的消息,那就能够不发送)
  4. 释放该项资源时。进程Pi从自己的消息队列中删除(Tm,Pi)资源请求。同一时候给其它全部进程发送一个带有时间戳的Pi资源释放消息
  5. 当进程Pj收到Pi资源释放消息后,它就从自己的消息队列中删除(Tm,Pi)资源请求
  6. 当同一时候满足例如以下两个条件时,就将资源分配给进程Pi:

a) 依照“=>”关系排序后。(Tm,Pi)资源请求排在它的请求队列的最前面

b) Pi已经从全部其它进程都收到了时间戳>Tm的消息

为什么这个算法能够保证日志server能被依照正确的顺序分配呢?细致想一想5中的a b两个条件,仅仅有当一个进程收到全部其它进程>Tm的消息后才会对自己的队列进行排序。

那么如果其它进程在他之前请求了这个资源,可是由于网络慢还没收到,怎么办?由于前面已经提出如果,对于随意的两个进程Pi和Pj,它们之间传递的消息是依照发送顺序被接收到的。

所以既然收到>Tm消息,那么说明其它全部进程在Tm之前的消息也都已经被收到了,所以这个时候自己的队列中肯定已经收到了全部的对资源的请求。这个时候仅仅须要依照“=>”关系排序。排在最前面的就是最先发出请求的。

可见利用Logical Clock确实能够解决这样的问题。那么我们再来看还有一种问题。如果京东的后台架构例如以下图所看到的:

图四  还有一种情况

有多台前端代理server。用户请求会随机地分配到各个代理server上。

假设小明在物理时间7点50下单买了一本书,大明在7点51分下单,正好书仅仅有一本。小明的请求被分配到了serverA,大明的被分配到了serverB。假设在这之前,A的Logical Clock走到了200。而B的Logical Clock走到150。那么在这个情况下。假设运用上述算法,B会先获得资源,下单买到票。所以从道理上说。这个算法是不公平的,可是换个角度想想,这样充其量也仅仅是不公平。不会导致系统发生因果错误。由于小明和大明的请求在系统看来是并发事件,没有因果关系,所以系统无法判定并发事件的真实顺序。

这就是之前我说的“这种方法给出了一个定义分布式系统中事件顺序的方法,他确保的是全部因果关系的事件不会发生逻辑错误,但他并不保证系统的公平性(比方两台server同一时候并发地请求一个资源。物理时间上先发出请求的进程不一定会先得到这个资源。但这顶多会造成不公平,不会造成错误)。

參考文献:

  1. Lamport, L. (1978). “Time,
    clocks, and the ordering of events in a distributed system”
  2. Why vector clocks are more powerful than Lamport
    timestamps
  3. Lamport论文中文翻译加感想
  4. 数据一致性: 时间戳策略
  5. 分布式数据库的数据版本号合并
原文地址:http://www.orzace.com/lamport-logical-clock/

我对Lamport Logical Clock的理解的更多相关文章

  1. Lamport Logical Clock 学习

    1,导论 ①如何在分布式环境下定义系统中所有事件的发生顺序?②分布式环境下多个进程竞争资源时如何互斥?③什么是偏序,偏序的作用是什么,有什么不足?④什么是全序,全序的作用是什么,有什么不足?⑤为什么需 ...

  2. 分布式系统根基:物理时钟和Lamport逻辑时钟

    分布式系统解决了传统单体架构的单点问题和性能容量问题,另一方面也带来了很多的问题,其中一个问题就是多节点的时间同步问题:不同机器上的物理时钟难以同步,导致无法区分在分布式系统中多个节点的事件时序.19 ...

  3. [源码解析] 并行分布式框架 Celery 之 Lamport 逻辑时钟 & Mingle

    [源码解析] 并行分布式框架 Celery 之 Lamport 逻辑时钟 & Mingle 目录 [源码解析] 并行分布式框架 Celery 之 Lamport 逻辑时钟 & Ming ...

  4. Vector Clock/Version Clock

    physical clock 机器上的物理时钟,不同的机器在同一个时间点取到的physical clock不一样,之间会存在一定的误差,NTP可以用来控制这个误差,同一个机房内的机器之间的时钟误差可以 ...

  5. 《In Search of an Understandable Consensus Algorithm》翻译

    Abstract Raft是一种用于管理replicated log的consensus algorithm.它能和Paxos产生同样的结果,有着和Paxos同样的性能,但是结构却不同于Paxos:它 ...

  6. Raft

    http://thesecretlivesofdata.com/raft/ https://github.com/coreos/etcd   1 Introduction Consensus algo ...

  7. Java内存模型-jsr133规范介绍

    原文地址:http://www.cnblogs.com/aigongsi/archive/2012/04/26/2470296.html; 近期在看<深入理解Java虚拟机:JVM高级特性与最佳 ...

  8. Raft翻译

    英文原文:https://web.stanford.edu/~ouster/cgi-bin/papers/raft-atc14 In Search of an Understandable Conse ...

  9. [源码解析] 并行分布式任务队列 Celery 之 EventDispatcher & Event 组件

    [源码解析] 并行分布式任务队列 Celery 之 EventDispatcher & Event 组件 目录 [源码解析] 并行分布式任务队列 Celery 之 EventDispatche ...

随机推荐

  1. Andorid 生成NDK动态链接库 .so库

    .so库第一次见到是在搜索Android保存静态秘钥等特殊id字段做法时看到的-通过NDK的方式将静态秘钥保存在so文件中, 关于原生开发工具包(NDK)详细见官网指南要更详细,这里我记录我度娘各种结 ...

  2. 面试中注意3个javascript的问题

    JavaScript 是所有现代浏览器的官方语言.因此,各种语言的开发者面试中都会遇到 JavaScript 问题. 本文不讲最新的 JavaScript 库,通用开发实践,或任何新的 ES6 函数. ...

  3. 设计模式之单例模式 Singleton

    核心作用 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点. 常见应用场景 优点 由于单例模式只生成一个实例,减少了系统性开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对 ...

  4. STP

    生成树协议  spanning-tree protocol     网络中额外添加的链路连接着路由器和交换机 会引起流量的环路   当一个交换机的连接丢失时 另一条链路能快速地取代失败的链路  并且不 ...

  5. VirtualBox 安装XP虚拟机, 安装DB2

    个人随笔记录,也许说的不太清楚. 1. 用google搜索VirtualBox, 找到下载地址,下载,我的是win7,下载64bit的. 2. 下载后,安装VBox软件,这个没遇到问题. 3. 因为我 ...

  6. ZOJ-3953 Intervals,t

    Intervals 题意:给出n个区间,求最少删除多少个区间使得任意三个区间都不相交. 思路:按区间左端点排序,每次选取r最大的两个与当前比较,如果能放更新r,否则删除r最大的.关键就在怎么删除r最大 ...

  7. js原型链继承的傻瓜式详解

    本文争取用最简单的语言来讲解原型链继承的OOP原理 0.如果对原型继承还没有大致了解,完全一头雾水,请先阅读 <JavaScript高级程序设计>第六章最后部分的寄生组合式继承 或者_廖雪 ...

  8. 利用Python访问Mysql数据库

    首先要明确一点,我们在Python中需要通过第三方库才能访问Mysql. 有这样几种方式:Mysql-python(即MySQLdb).pymysql.mysql-connector.Mysql-py ...

  9. 【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的邻居 旋转坐标系+并查集+Treap/STL-set

    题目描述 了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个“群”.每只奶牛在吃草的时候有一个独一无二的位置坐标Xi,Yi(l≤Xi,Yi≤ ...

  10. 【Luogu】P2445动物园(最大流)

    题目链接 题目本身还是比较水的吧……容易发现是最大流套上dinic跑一遍就好了,并不会超时. 比较不偷税的一点是关于某动物的所有目击报告都符合才能连边……qwqqwqqwq #include<c ...