現實生活中時間是很重要的概念,時間能夠記錄事情發生的時刻、比較事情發生的前後順序。分佈式系統的一些場景也須要記錄和比較不一樣節點間事件發生的順序,但不一樣於平常生活使用物理時鐘記錄時間,分佈式系統使用邏輯時鐘記錄事件順序關係,下面咱們來看分佈式系統中幾種常見的邏輯時鐘。html
物理時鐘 vs 邏輯時鐘bash
可能有人會問,爲何分佈式系統不使用物理時鐘(physical clock)記錄事件?每一個事件對應打上一個時間戳,當須要比較順序的時候比較相應時間戳就行了。app
這是由於現實生活中物理時間有統一的標準,而分佈式系統中每一個節點記錄的時間並不同,即便設置了 NTP 時間同步節點間也存在毫秒級別的誤差[1][2]。於是分佈式系統須要有另外的方法記錄事件順序關係,這就是邏輯時鐘(logical clock)。分佈式
Lamport timestampside
Leslie Lamport 在1978年提出邏輯時鐘的概念,並描述了一種邏輯時鐘的表示方法,這個方法被稱爲Lamport時間戳(Lamport timestamps)[3]。wordpress
分佈式系統中按是否存在節點交互可分爲三類事件,一類發生於節點內部,二是發送事件,三是接收事件。Lamport時間戳原理以下:post
圖1: Lamport timestamps space time (圖片來源: wikipedia)ui
假設有事件a、b,C(a)、C(b)分別表示事件a、b對應的Lamport時間戳,若是C(a) < C(b),則有a發生在b以前(happened before),記做 a -> b,例如圖1中有 C1 -> B1。經過該定義,事件集中Lamport時間戳不等的事件可進行比較,咱們得到事件的偏序關係(partial order)。spa
若是C(a) = C(b),那a、b事件的順序又是怎樣的?假設a、b分別在節點P、Q上發生,Pi、Qj分別表示咱們給P、Q的編號,若是 C(a) = C(b) 而且 Pi < Qj,一樣定義爲a發生在b以前,記做 a => b。假如咱們對圖1的A、B、C分別編號Ai = 一、Bj = 二、Ck = 3,因 C(B4) = C(C3) 而且 Bj < Ck,則 B4 => C3。3d
經過以上定義,咱們能夠對全部事件排序、得到事件的全序關係(total order)。上圖例子,咱們能夠從C1到A4進行排序。
Vector clock
Lamport時間戳幫助咱們獲得事件順序關係,但還有一種順序關係不能用Lamport時間戳很好地表示出來,那就是同時發生關係(concurrent)[4]。例如圖1中事件B4和事件C3沒有因果關係,屬於同時發生事件,但Lamport時間戳定義二者有前後順序。
Vector clock是在Lamport時間戳基礎上演進的另外一種邏輯時鐘方法,它經過vector結構不但記錄本節點的Lamport時間戳,同時也記錄了其餘節點的Lamport時間戳[5][6]。Vector clock的原理與Lamport時間戳相似,使用圖例以下:
圖2: Vector clock space time (圖片來源: wikipedia)
假設有事件a、b分別在節點P、Q上發生,Vector clock分別爲Ta、Tb,若是 Tb[Q] > Ta[Q] 而且 Tb[P] >= Ta[P],則a發生於b以前,記做 a -> b。到目前爲止還和Lamport時間戳差異不大,那Vector clock怎麼判別同時發生關係呢?
若是 Tb[Q] > Ta[Q] 而且 Tb[P] < Ta[P],則認爲a、b同時發生,記做 a <-> b。例如圖2中節點B上的第4個事件 (A:2,B:4,C:1) 與節點C上的第2個事件 (B:3,C:2) 沒有因果關係、屬於同時發生事件。
Version vector
基於Vector clock咱們能夠得到任意兩個事件的順序關係,結果或爲前後順序或爲同時發生,識別事件順序在工程實踐中有很重要的引伸應用,最多見的應用是發現數據衝突(detect conflict)。
分佈式系統中數據通常存在多個副本(replication),多個副本可能被同時更新,這會引發副本間數據不一致[7],Version vector的實現與Vector clock很是相似[8],目的用於發現數據衝突[9]。下面經過一個例子說明Version vector的用法[10]:
圖3: Version vector
Vector clock只用於發現數據衝突,不能解決數據衝突。如何解決數據衝突因場景而異,具體方法有以最後更新爲準(last write win),或將衝突的數據交給client由client端決定如何處理,或經過quorum決議事先避免數據衝突的狀況發生[11]。
因爲記錄了全部數據在全部節點上的邏輯時鐘信息,Vector clock和Version vector在實際應用中可能面臨的一個問題是vector過大,用於數據管理的元數據(meta data)甚至大於數據自己[12]。
解決該問題的方法是使用server id取代client id建立vector (由於server的數量相對client穩定),或設定最大的size、若是超過該size值則淘汰最舊的vector信息[10][13]。
小結
以上介紹了分佈式系統裏邏輯時鐘的表示方法,經過Lamport timestamps能夠創建事件的全序關係,經過Vector clock能夠比較任意兩個事件的順序關係而且能表示無因果關係的事件,將Vector clock的方法用於發現數據版本衝突,因而有了Version vector。
[1] Time is an illusion, George Neville-Neil, 2016
[2] There is No Now, Justin Sheehy, 2015
[3] Time, Clocks, and the Ordering of Events in a Distributed System, Leslie Lamport, 1978
[4] Timestamps in Message-Passing Systems That Preserve the Partial Ordering, Colin J. Fidge, 1988
[5] Virtual Time and Global States of Distributed Systems, Friedemann Mattern, 1988
[6] Why Vector Clocks are Easy, Bryan Fink, 2010
[7] Conflict Management, CouchDB
[8] Version Vectors are not Vector Clocks, Carlos Baquero, 2011
[9] Detection of Mutual Inconsistency in Distributed Systems, IEEE Transactions on Software Engineering , 1983
[10] Dynamo: Amazon’s Highly Available Key-value Store, Amazon, 2007
[11] Conflict Resolution, Jeff Darcy , 2010
[12] Why Vector Clocks Are Hard, Justin Sheehy, 2010
[13] Causality Is Expensive (and What To Do About It), Peter Bailis ,2014