向量時鐘演算法

weixin_33866037發表於2017-10-28

【轉自】http://blog.chinaunix.net/uid-27105712-id-5612512.html

一、使用背景

先說一下需要用到向量時鐘的場景。我們在寫資料時候,經常希望資料不要儲存在單點。如db1,db2都可以同時提供寫服務,並且都存有全量資料。而client不管是寫哪一個db都不用擔心資料寫亂問題。但是現實場景中往往會碰到並行同時修改。導致db1和db2資料不一致。於是乎就有人想出一些解決策略。向量時鐘算是其中一種。簡單易懂。但是並沒有徹底解決衝突問題,現實分散式儲存補充了很多額外技巧。

這裡反向敘述方式, 介紹向量時鐘。先舉實際例子讓讀者有個感性認識,然後再說演算法規則。

二、舉個例子

向量時鐘實際是一組版本號(版本號=邏輯時鐘),假設資料需要存放3份,需要3臺db儲存(用A,B,C表示),那麼向量維度就是3,每個db有一個版本號,從0開始,這樣就形成了一個向量版本[A:0, B:0, C:0];

Step 1: 初始狀態下,所有機器都是[A:0, B:0, C:0]

DB_A——> [A:0, B:0, C:0]

DB_B——> [A:0, B:0, C:0]

DB_C——> [A:0, B:0, C:0]

Step 2:  假設現在應用是一個商場,現在錄入一個腎6的價格iphone6 price 5888; 客戶端隨機選擇一個db機器寫入。現假設選擇了A,資料大概是這樣 :

{key=iphone_price; value=5888; vclk=[A:1,B:0,C:0]}

Step 3:  接下來A會把資料同步給BC;於是最終同步結果如下

DB_A——> {key=iphone_price; value=5888; vclk=[A:1,B:0,C:0]}

DB_B——> {key=iphone_price; value=6888; vclk=[A:1, B:0,C:0]}

DB_C——> {key=iphone_price; value=5888; vclk=[A:1,B:0,C:0]}

Step 4:過了分鐘,價格出現波動,升值到6888;於是某個業務員更新價格。這時候系統隨機選擇了B做為寫入儲存,於是結果看起來是這樣:

DB_A——> {key=iphone_price; value=5888; vclk=[A:1,B:0,C:0]}

DB_B——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

DB_C——> {key=iphone_price; value=5888; vclk=[A:1,B:0,C:0]}

Step 5:於是B就把更新同步給其他幾個儲存

DB_A——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

DB_B——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

DB_C——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

到目前為止都是正常同步,下面開始演示一下不正常的情況。

Step 6:價格再次發生波動,變成4000,這次選擇C寫入:

DB_A——> {key=iphone_price; value=6888; vclk=[A:1, B:1,C:0]}

DB_B——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

DB_C——> {key=iphone_price; value=4000; vclk=[A:1, B:1,C:1]}

Step 7:  C把更新同步給AB,因為某些問題,只同步到A,結果如下:

DB_A——> {key=iphone_price; value=4000; vclk=[A:1, B:1,C:1]}

DB_B——> {key=iphone_price; value=6888; vclk=[A:1,B:1,C:0]}

DB_C——> {key=iphone_price; value=4000; vclk=[A:1, B:1,C:1]}

Step 8:價格再次波動,變成6000元,系統選擇B寫入

DB_A——> {key=iphone_price; value=6888; vclk=[A:1, B:1,C:1]}

DB_B——> {key=iphone_price; value=6000; vclk=[A:1,B:2, C:0]}

DB_C——> {key=iphone_price; value=4000; vclk=[A:1, B:1,C:1]}

Step 9: 當B同步更新給A和C時候就出現問題了,A自己的向量時鐘是[A:1, B:1,C:1], 而收到更新訊息攜帶過來的向量時鐘是[A:1,B:2, C:0], B:2 比B:1新,但是C:0卻比C1舊。這時候發生不一致衝突。不一致問題如何解決?向量時鐘策略並沒有給出解決版本,留給使用者自己去解決,只是告訴你目前資料存在衝突

三、規則介紹

版本號變更規則其實就2條,比較簡單

1、   每次修改資料,本節點的版本號 加1,例如上述step 8中 向B寫入,於是從B:1變成B:2,其他節點的版本號不發生變更。

2、   每次同步資料(這裡需要注意,同步和修改是不一樣的寫操作哦),會有三種情況:

a: 本節點的向量版本都要比訊息攜帶過來的向量版本低(小於或等於) 如本節點為[A:1, B:2,C:3]}, 訊息攜帶過來為[A:1, B:2,C:4]或[A:2, B:3,C:4]等。 這時候合併規則取每個分量的最大值。

b:   本節點的向量版本都要比比訊息攜帶過來的向量版本高,這時候可以認為本地資料比同步過來的資料要新,直接丟棄要同步的版本。

c:   出現衝突,如上述step 9中,有的分量版本大,有的分量版本小,無法判斷出來到底誰是最新版本。就要進行衝突仲裁。

四、衝突解決

其實沒有一個比較好的解決衝突的版本:就筆者目前所瞭解,加上時間戳算是一個策略。具體方法是再加一個維度資訊:資料更新的時間戳(timestamp)。[A:1, B:2,C:4,ts:123434354] ,如果發生衝突,再比較一下兩個資料的ts,大的數值說明比較後更新,選擇它作為最終資料。並對向量時鐘進行訂正。

五、其他問題

1、向量時鐘的維數和存放資料備份數目相等,如果備份數目太多。會導致向量太長。不過目前好像不會存在這個問題,一般備份數目=3就足夠。即使再多幾份,也不會太長。

2、 衝突糾錯時,矯正方有很多:有的放在後臺服務端矯正,有的交給客戶端來矯正,譬如客戶端仲裁後,寫回服務端。糾錯時機也有很多,有點在讀資料是發現資料不一致進行糾正,有的是同步時候發現不一致糾正。實際實現大家自己選擇。

相關文章