蘭波特時鐘(Lamport clock)
/ LAM-port /
如果你無法信任不同機器上的牆上時鐘(而在分散式系統裡,你確實無法),那你到底要怎麼說一個事件發生在另一個之前?Leslie Lamport(蘭波特)巧妙的答案是:別再試著去量真實時間,改成單純地「計數」,並且用一種尊重因果的方式來計數。蘭波特時鐘並不是一個告訴你幾點的時鐘——它是每台機器上的一個簡單計數器,讓整個系統能在「順序真的重要」時,對事件的順序取得共識,而完全不需要同步過的實體時間。
規則簡單得令人驚喜。每個行程都維護一個從 0 開始的計數器。(1) 在它做任何本地事件之前,先把計數器加 1,並用這個數字替該事件蓋章。(2) 當它送出一個訊息時,附上它目前的計數器值。(3) 當它收到一個訊息時,把計數器設為「自己的值」與「訊息裡的值」兩者中較大的那個,然後再加 1。這個效果捕捉了「發生在先」關係:如果事件 X 有可能造成了事件 Y——因為它們依序在同一個行程上、或因為 X 送了一個被 Y 收到的訊息——那麼 X 的時間戳記就保證會小於 Y 的。有因果關係的事件,就會以正確的順序排出來。
為什麼重要,以及一個誠實的限制:蘭波特時鐘讓你只用計數器、加上你本來就在送的那些訊息,就能對有因果關係的事件取得一個一致的、全系統的順序——不必原子鐘、不必 NTP。這是許多分散式演算法的骨幹。但有個你必須尊重的陷阱:這個蘊涵只成立於一個方向。如果 X 發生在先於 Y,那 X 的數字較小;但較小的數字「並不」證明 X 真的發生在先於 Y——兩個互不相干的並行事件,數字的大小可以是任意順序。要偵測真正的並行(互不影響的事件),你需要更豐富的向量時鐘,它替每個行程各保留一個計數器,而不是單一個數字。
行程 A 做了一個事件(計數器 1),接著送出一個帶著 2 的訊息。行程 B 的計數器原本在 5;收到訊息時,它取 max(5, 2) + 1 = 6。於是那個送出(蓋章 2)就正確地排在那個收到(蓋章 6)之前。但 B 上一個無關的、蓋章 3 的事件,和 A 那個蓋章 2 的送出,只是並行而已——數字無法告訴你它們之間沒有因果關連。
尊重因果的計數器:若 X 可能造成了 Y,X 的章就較小。反過來則不保證。
蘭波特時鐘排序的是有因果關係的事件、而非真實時間,而且較小的時間戳記並不證明真有先後——並行事件兩種比較結果都可能出現。當你必須真的偵測並行時,請用向量時鐘。