分佈式系統:時間、時鐘和事件序列

在程序中,咱們常常須要知道事件序列,在單體應用中,事件序列是較爲簡單的,最簡單的辦法就是用時間戳,但在分佈式系統中,事件序列是很困難的,Leslie Lamport大神在論文Time, Clocks, and the Ordering of Events in a Distributed System討論了在分佈式系統中時間、時鐘和事件序列的問題。html

【1】分佈式系統中物理時鐘存在的問題

邏輯時鐘是相對物理時鐘這個概念的,爲何要提出邏輯時鐘,由於物理時鐘在分佈式系統中存在一系列問題。在一臺機器上的多個進程能夠從一個物理時鐘中獲取時間戳,無論這個物理時鐘是否準確,只要是從一個物理時鐘獲取時間戳,咱們都能得到多個事件的相對時間順序。可是在分佈式系統中,咱們沒法從一個物理時鐘獲取時間戳,只能從各自機器上物理時鐘獲取時間戳,而各臺機器的物理時鐘是很難徹底同步的,即便有NTP,精度也是有限的。因此在分佈式系統中,是不能經過物理時鐘決定事件序列的。web

物理時鐘在分佈式系統中也不是毫無用處,至少它必定程度上能夠判斷在一臺機器上的事件順序,同時分佈式系統中仍是有必要讓不一樣機器上的物理時鐘在必定精度內同步時間的,只是不做爲決定事件序列的方法。算法

【2】偏序(Partial Ordering)

事件序列有兩種:偏序事件序列和全序事件序列。所謂的偏序指的是隻能爲系統中的部分事件定義前後順序。這裏的部分實際上是有因果關係的事件。在論文Time, Clocks, and the Ordering of Events in a Distributed System中,偏序是由「happened before」引出的,咱們先看一下"happened before"(表示爲「->」)的定義:微信

Definition. The relation "->"on the set of events of a system is the smallest relation satisfying the following three conditions:
(1) If a and b are events in the same process, and a comes before b, then a->b.
(2) If a is the sending of a message by one process and b is the receipt of the same message by another process, then a->b.
(3) If a->b and b->c then a->c.網絡

在分佈式系統中,只有兩個發生關聯的事件(有因果關係),咱們纔會去關心二者的先來後到關係。對於併發事件,他們兩個誰先發生,誰後發生,其實咱們並不關心。偏序就是用來定義兩個因果事件的發生次序,即‘happens before’。而對於併發事件(沒有因果關係),並不能決定其前後,因此說這種‘happens before’的關係,是一種偏序關係。併發

If two entities do not exchange any messages, then they probably do not need to share a common clock; events occurring on those entities are termed as concurrent events.」app

【3】邏輯時鐘

論文原文中有這樣一句:We begin with an abstract point of view in which a clock is just a way of assigning a number to an event, where the number is thought of as the time at which the event occurred. 這句話的意思是,能夠把時間進行抽象,把時間值當作是事件發生順序的一個序列號,這個值能夠<20190515,20190516,20190517>,也能夠是<1,2,3>。後面就有了邏輯時鐘的概念。定義以下:
we define a clock Ci for each process Pi to be a function which assigns a number Ci(a) to any event a in that process.分佈式

Clock Condition. For any events a,b: if a->b then C(a) < C(b).
C1. If a and b are events in process Pi, and a comes before b, then Ci(a) < Ci(b).
C2. If a is the sending of a message by process Pi and b is the receipt of that message by process Pi, then Ci(a) < Ci(b).操作系統

具體的,根據上面的定義條件,咱們作以下實現規則:.net

  • 每一個事件對應一個Lamport時間戳,初始值爲0
  • 若是事件在節點內發生,本地進程中的時間戳加1
  • 若是事件屬於發送事件,本地進程中的時間戳加1並在消息中帶上該時間戳
  • 若是事件屬於接收事件,本地進程中的時間戳 = Max(本地時間戳,消息中的時間戳) + 1
    在這裏插入圖片描述

根據上面的定義,咱們知道a->bC(a)<C(b),但若是C(a)=C(b),那麼a,b是什麼順序呢?它們確定不是因果關係,因此它們之間的前後其實並不會影響結果,咱們這裏只須要給出一種肯定的方式來定義它們之間的前後就能獲得全序關係。

一種可行的方式是利用給進程編號,利用進程編號的大小來排序。假設a、b分別在節點P、Q上發生,Pi、Qj分別表示咱們給P、Q的編號,若是 C(a)=C(b) 而且 Pi<Qj,一樣定義爲a發生在b以前,記做 a⇒b(全序關係)。假如咱們上圖的A、B、C分別編號Ai=一、Bj=二、Ck=3,因 C(B4)=C(C3) 而且 Bj<Ck,則 B4⇒C3

經過以上定義,咱們能夠對全部事件排序,得到事件的全序關係(total order)。上圖例子,咱們能夠進行排序:C1⇒B1⇒B2⇒A1⇒B3⇒A2⇒C2⇒B4⇒C3⇒A3⇒B5⇒C4⇒C5⇒A4。觀察上面的全序關係你能夠發現,從時間軸來看B5是早於A3發生的,可是在全序關係裏面咱們根據上面的定義給出的倒是A3早於B5,這是由於Lamport邏輯時鐘只保證因果關係(偏序)的正確性,不保證絕對時序的正確性。

【4】嘗試用邏輯時鐘解決分佈式鎖的問題

單機多進程程序可由鎖進行同步,那是由於這些進程都運行在操做系統上,有center爲它們的請求排序,這個center知道全部須要進行同步的進程的全部信息。可是在分佈式系統中,各個進程運行在各自的主機上,沒有一個center的概念,那分佈式系統中多進程該怎麼進行同步呢?或者說分佈式鎖該怎麼實現呢?論文中提出瞭解決這一問題的算法要知足下面三個條件:
(I) A process which has been granted the resource must release it before it can be granted to another process.
(II) Different requests for the resource must be granted in the order in which they are made.
(III) If every process which is granted the resource eventually releases it, then every request is eventually granted. 爲了簡化問題,咱們作以下假設:

  • 任何兩個進程PiPj它們之間接收到的消息的順序與發送消息的順序一致,而且每一個消息必定可以被接收到。
  • 每一個進程都維護一個不被其餘進程所知的請求隊列。而且請求隊列初始化爲包含一個T0:P0請求,P0用於該共享資源,T0是初始值小於任什麼時候鍾值

算法以下:

  1. To request the resource, process Pi sends the message Tm:Pi requests resource to every other process, and puts that message on its request queue, where Tm is the timestamp of the message.(請求資源,發送請求給其餘進程,在本身的請求隊列中添加該請求)
  2. When process Pj receives the message Tm:Pi requests resource, it places it on its request queue and sends a (timestamped) acknowledgment message to Pi.(收到其餘進程的請求,放到請求隊列中,迴應發起請求的進程)
  3. To release the resource, process Pi removes any Tm:Pi requests resource message from its request queue and sends a (timestamped) Pi releases resource message to every other process.(釋放資源,從請求隊列中移除該資源請求,發送給其餘進程,告訴它們我釋放了該資源)
  4. When process Pj receives a Pi releases resource message, it removes any Tm:Pi requests resource message from its request queue.(收到其餘進程釋放資源的消息,從請求隊列中移除該資源請求)
  5. Process Pi granted the resource when the following two conditions are satisfied: (i) There is a Tm:Pi requests resource message in its request queue which is ordered before any other request in its queue by the relation . (ii) Pi has received a message from every other process timestamped later than Tm. (判斷本身是否能夠得到該資源,有兩個條件:其一,按全序排序後,Tm:Pi請求在請求隊列的最前面;其二,本身Pi已經收到了全部其餘進程的時戳大於Tm的消息)

下面咱們舉個例子說明上面的算法過程: 初始狀態爲P0擁有資源,請求隊列中爲0:0(T0:P0的簡寫),然後P1請求資源,將1:1添加到請求隊列中,此時P0讓佔有資源,P1還沒法獲取資源,等到P0釋放資源後,0:0從請求隊列中移除(下圖中沒有畫出),此時請求隊列中1:1的請求在最前面,同時P1收到了其餘兩個進程的大於1的迴應消息,知足了佔有資源的條件,此時P1佔有資源。

在這裏插入圖片描述
其實關鍵思想很簡單,既然分佈式系統中沒有「center」的概念,那我請求共享資源時我就讓其餘全部進程都知道我要請求該資源,擁有資源的進程釋放資源時也告訴全部進程,我要釋放該資源,想請求該資源的大家能夠按序(邏輯時鐘的做用,這裏再次說明一下,並不能保證在絕對物理時間上請求的排序)請求了。這樣每一個進程都知道其餘進程的狀態,就至關於有個「center」。

對於分佈式鎖問題,多個請求不必定是必定按照絕對物理時鐘排序才能夠,只要咱們有這樣一個算法,這個算法能夠保證多個進程的請求按照這個算法總能獲得同一個排序,就能夠了,按照絕對物理時鐘排序只是其中一個可行的算法。

到這裏是否就萬事大吉了呢,其實並無,這個實現是很脆弱的,它要求全部進程都很是可靠,一旦一個進程掛了或出現網絡分區的狀況,是沒法工做的,同時咱們提出的網絡要求也很是嚴格,要求發出的消息必定被接收到,這個在實用的系統中是很難作到的。因此這是一個理想情況下的算法實現,並非一個能夠工業級應用的算法實現。但它仍然是很是有意義的,給了咱們關於分佈式系統中解決一致性、共識算法等思想啓迪。

參考文檔:
大神論文:Time, Clocks, and the Ordering of Events in a Distributed System
Lamport timestamps
分佈式系統:Lamport 邏輯時鐘

關注微信公衆號,天天進步一點點!

相關文章
相關標籤/搜索