1. 程式人生 > >我對Lamport Logical Clock的理解

我對Lamport Logical Clock的理解

進程 cal 這一 width 排序 之前 能夠 margin 兩個

建議先看論文原文再來看這篇文章,我不會對論文中的各個點都具體說明。僅僅是寫一些我自己的想法,幫助理解。

大家都知道。分布式環境下。確定各個事件發生的順序非常重要,不然就會發生一些麻煩的問題。

考慮一下這個問題:小明要用同學給的優惠碼在京東上買一本書。如果京東的後臺架構如圖一所看到的。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的理解