分佈式系統時鐘和有序-(3)

前言

分佈式系統本質上就是使用多機來解決單機的問題,爲了保證它的通用性,必須能夠保證如單機一樣的order。所有你所關心的就是它確實像單機一樣在執行操作。本章主要講解分佈式系統一個基本問題:時序問題。

如果你還不瞭解分佈式系統,那麼歡迎戳它:分佈式系統基本概念-(1)
如果你想進一步瞭解分佈式系統的基本特徵以及consensus問題,那麼歡迎戳它:分佈式系統上下層概念抽象-(2)

1. 全序和偏序

這說明的分佈式系統最重要的順序問題。從單機角度上來,不同任務之間可以具有完全的order,然後從多機系統的角度,我們想卻無法保證全局的Order。

全序:集合中的任何兩個元素都是可比較的(例如實數域);
偏序:集合中只有部分元素是是可比較的(例如複數域)。

對於全序和偏序來說,都具有反對稱性和傳遞性
也就是說對於任意的a和b,有

If a ≤ b and b ≤ a then a = b (antisymmetry);
If a ≤ b and b ≤ c then a ≤ c (transitivity);

不同之處,對於偏序來說,具有自反性,即

a ≤ a (reflexivity) for all a in X

對於全序來說,具有完全性,即

a ≤ b or b ≤ a (totality) for all a, b in X

不知道上面的性質有沒有把你說懵逼?
反對稱性和傳遞性都比較容易理解。舉例集合的包含關係,如果集合a包含集合b,且集合b包含集合a,那麼a=b(反對稱性),如果集合a包含b,b包含c,那麼集合a包含c(傳遞性)。

對於全序來說,具有完全性,也就是說任意的兩個元素都具有比較關係,即要不a>= b,要不b>=a,至少一個成立;對於偏序來說,並不能滿足任意成立,只需要滿足自反性即可。可以看出,偏序包含全序,偏序是一種更泛化的情況。

以git爲例,在一個分支中,git log中顯示的版本號具有序號的概念,可比較(例如先提交,後提交之說),然後不同branch之間的git提交卻是無法比較的。例如都是基於master,創建了分支A和B,然後

分支A: a1, a2, a3 ...
分支B: b1, b2, b3 ...

雖然分支A和B起源於同一個位置,然後A和B向不同方向發展,因此不可比較,這叫發生了分叉,也就是說全序不再成立。對於分叉的處理要不自動合併,要不人工選擇某一個。

2. 什麼是time

time具有三種屬性:

  • Order:決定事件的順序
  • Duration:用於測量the amount of time
  • Interpretation:便於解釋,例如轉換爲Date,具有具體含義

分佈式系統的各個節點內部都具有自身的Order(可以依靠自身的時鐘),但是由於它們獨立的運行,無法得到全局的Order(各個節點的clock可能不完全一致)。

3. time走的一樣快嗎?

對於這個問題,有三種角度:

  • Global clock:yes
  • Local clock:no,but
  • No clock:no

Global clock

假設整個系統有一個全局的精確時鐘,所有event的發生的時間戳都被精確的確定,因此就具有了全局的Order,然而這是一個理想的系統。真實情況下,時鐘同步會有一定的延遲,用戶也可能手動修改了時鐘,這都會導致各個節點的時間不一致。

即使這樣,仍然有系統假設時鐘是完美同步的,例如Facebook的Cassandra基於時鐘同步假設,它解決衝突的標準是選擇最新的寫入(最新基於時鐘最新)。

Local clock

Local clock假設感覺更可行一些,它說的是每個機器都有本地的時鐘,並沒有全局的時鐘。Local clock定義了一種偏序關係,在節點內部,event是有序的,然而節點之間,event是不可比較的。

No clock

No clock意味着沒有邏輯時間的概念,取而代之的是我們使用couters和communication來決定事件是否發生過,異或是還未發生。 由於沒有time的概念,我們也就無法使用timeout。這也是一個偏序概念:從節點本身來看,可以使用counter決定順序,跨節點的事件順序則可以使用communication。如果沒有使用時鐘,那麼事件排序的精度取決於節點之間的通信延遲。

從分佈式系統的角度來說,time非常重要。它可以用來決定事件的順序,從而能夠確保執行結果的正確性,因爲很多agreement需要依賴事件的Order來達成一致。如果有global clock,那麼不同節點上的event排序就顯得非常容易,無需通信,而在沒有global clock的情況下,節點之間只能依賴communication決定Ordering。

4. Vector clocks (time for causal order)

Lamport的時鐘取代了物理時鐘,它依賴counter和communication來決定事件的發何時能順序,所有的進程遵循以下規則:

  • 進程執行一個操作,增加counter
  • 進程發送一條消息,攜帶當前的counter
  • 進程收到一條消息,更新自己的counter爲max(local_counter, received_counter)+1

它可使用以下代碼來表示:

function LamportClock() {
	this.value = 1;
} 
LamportClock.prototype.get = function() {
	return this.value;
} 
LamportClock.prototype.increment = function() {
	this.value++;
} 
LamportClock.prototype.merge = function(other) {
	this.value = Math.max(this.value, other.value) + 1;
}

Lamport clock使得跨節點之間的counter比較成爲可能,如果timestamp(a) < timestamp(b),則有兩種可能:

  • a發生在b之前,或者
  • a和b不可比較

從單個機器來說,先發送a消息,然後收到b消息,那麼可以確保a在b之前;從兩個機器來說,一個機器收到a消息,另外一個收到b消息,對於這兩個消息賦值相應的時間戳,即使ts(a) < ts(b),我們也無法說明a發生在b之前,因爲在這種情況下,a和b比較是無意義的。

向量鎖

向量鎖是Lamport clock的擴展,它維護着一個array序列[t1, t2, …, tn],其中一個節點一個ts。與前面的Lamport clock的更新不同,這次每個節點只更新屬於自己的那個counter,具體來說:

  • 進程進行一個操作,增加array中屬於自己的counter;
  • 進程發送消息,攜帶整個array;
  • 進程接收消息,更新array的每個元素爲最大值,然後增加屬於自己的counter。

代碼如下:

function VectorClock(value) {
	// expressed as a hash keyed by node id: e.g. { node1: 1, node2: 3 }
	this.value = value || {};
} 
VectorClock.prototype.get = function() {
	return this.value;
};
//只對屬於自己的counter增加1
VectorClock.prototype.increment = function(nodeId) {
	if(typeof this.value[nodeId] == 'undefined') {
		this.value[nodeId] = 1;
	} else {
		this.value[nodeId]++;
	}
};
// 作者手誤?感覺少增加一個1
VectorClock.prototype.merge = function(other) {
	var result = {}, last,
	a = this.value,
	b = other.value;
	// This filters out duplicate keys in the hash
	(Object.keys(a)
	.concat(b))
	.sort()
	.filter(function(key) {
		var isDuplicate = (key == last);
		last = key;
		return !isDuplicate;
	}).forEach(function(key) {
		result[key] = Math.max(a[key] || 0, b[key] || 0);
	});
	this.value = result;
};

借用wiki的一幅圖片:簡單說說來就是每個進程只負責增加自己的counter,每次收到消息和執行操作,就增加counter。個人還不是完全理解其本質,感覺上通過這種方式對通信和事件都基於counter,從而確定了有序性?

在這裏插入圖片描述

上面使用counter代替了global clock來實現了事件的有序性,那麼time的第二個屬性duration如何來操作呢?

5. Failure Detectors

當收不到回覆的消息,如何判斷這是由於remote node故障,還是由於high network latency導致的?這種一般需要設置一個最長超時時間。這個時間長短的設定本質上是在completeness和accuracy之間權衡。

完整性:crashed process被全部檢測到

  • Strong completeness.
    每個crashed process被所有正常的process檢測到
  • Weak completeness.
    每個crashed process最終只被部分正常的process檢測到

正確性:是否有正常的process被誤認爲crashed

  • Strong accuracy.
    沒有正常的process被誤認爲crashed
  • Weak accuracy.
    有一些正常的process被認爲crashed

完整性很容易達到,例如你可以等待無限制的時間,然後所有crashed process都會被最終檢測到。然而現實中,我們無法做到這種無限等待,只能做到weak failure detector,實現了weak completeness和weak accuracy。Chandra et al.證明weak failure detector已經足夠來解決consensus問題。

Time,Order和Performance

如果要求最好的時序,那麼可以使用同步語義,但是這樣性能就會有所犧牲。實際的系統需要根據時序的要求在可靠時序和性能方面進行權衡。

不同的系統對時序的要求不同,有些系統要求中間結果的每一步都是正確的,不能返回任何不一致的結果,有些系統並不要求中間過程(例如計算任務),只要確保結果最終正確就行。還有些情況,完全正確的結果並不一定重要,一個最好的結果估計也許就足夠,例如最好的電影評分的排名,是第一還是第二也許區別並不那麼大,the best effort result也許就足夠了。

6. Futher reading

Lamport clocks, vector clocks

  • [1] Time, Clocks and Ordering of Events in a Distributed System - Leslie Lamport, 1978 Failure detection

Snapshots

  • [1] Consistent global states of distributed systems: Fundamental concepts and mechanisms, Ozalp Babaogly and Keith Marzullo, 1993
  • [1] Distributed snapshots: Determining global states of distributed systems, K. Mani Chandy and Leslie Lamport, 1985

Causality

  • [1] Detecting Causal Relationships in Distributed Computations: In Search of the Holy Grail - Schwarz & Mattern, 1994
  • [1] Understanding the Limitations of Causally and Totally Ordered Communication - Cheriton & Skeen, 1993
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章