1. 程式人生 > >淺析Merkle Tree——分散式系統資料校驗的基石

淺析Merkle Tree——分散式系統資料校驗的基石

什麼是Merkle Tree

Merkle Tree是一種基於雜湊的資料結構。Merkle Tree是一種樹狀資料結構,該樹中的每一個葉子結點都是一個數據塊,而每一個非葉子結點都是其子結點組合的雜湊。普遍性況下Merkle Tree是二叉樹,也就是說Merkle Tree中的每一個結點有兩個子結點。當然,Merkle Tree可以是多叉樹,例如Ethereum平臺所採用的。簡單起見,本文我們僅討論二叉Merkle Tree。

Merkle Tree在分散式系統中被廣泛使用於進行資料校驗。一般來說,在分散式系統中,由於我們將資料儲存於許多不同的機器上,那麼為了保證資料可靠性及一致性,資料的校驗就顯得尤為重要。例如我們如果更新了一臺機器上的某一塊資料,這一更新必須被傳遞到分散式系統中的所有機器上以保證資料的最終一致性,這樣一來對比不同機器上的資料就是問題所在。

直接比較所有檔案顯然是一種既耗時又低效的方法。而且這樣做會由於機器之間相互發送檔案而產生大量的網路通訊。考慮到我們往往想要儘量減少通過網路傳送的資料量,傳送檔案的雜湊就成了自然而然的選擇。

Merkle Tree之所以能夠高效進行資料校驗,是因為其採用驗證雜湊值的形式進行校驗。相比於直接比較整個檔案,比較檔案雜湊值顯然高效的多。而且驗證雜湊值能夠大大減少分散式系統,或者peer-to-peer(p2p)系統中處於校驗需求所需要互相傳送的資訊量。

當前,Merkle Tree主要被用於Tor,Bitcoin,Git這一類的分散式p2p系統中,或用於Apache Cassandra或HBase這樣的分散式資料庫中。

Merkle Tree的結構綜述

如下圖所示是一個二叉Merkle Tree。此處我們假設圖中所使用的雜湊方法Hash()不會產生雜湊值碰撞的情況。也就是說,對於不同的輸入資料塊,Hash()一定會返回一個獨特的不會與其他資料塊對應雜湊值重疊的雜湊值,或說找到兩個具有相同雜湊值的不同區塊在現有計算機運算能力下不可實現。

二叉Merkle Tree示例

圖中最下層的四個葉節點表示四個資料塊。他們上層的abcd四個結點分別包含四個資料塊的雜湊。除去這兩層之外,Merkle Tree中其他結點的雜湊值均由其左右兩個子結點雜湊值組合計算得出。例如圖中ab結點的雜湊值由其兩個子結點——ab的雜湊值合併計算得出。以此類推,Merkle Tree中的每一個結點的雜湊值事實上反映了以該節點為根的子樹中所包含的所有資料塊的內容。

而通過以上分析,我們容易得知,構建Merkle Tree的過程其實就是從最底層的資料塊開始,合併左右兩個子結點的雜湊值並計算出他們的母結點的雜湊值,迴圈往復直至我們抵達該Merkle Tree的根結點,也就是當前層只剩下一個結點。

資料校驗的流程

當分散式系統中的電腦對應於一系列資料區塊構建出其Merkle Tree之後,我們可以通過如下演算法進行自上而下的資料校驗:
1. A伺服器向B伺服器傳送需要對應Merkle Tree的根結點的雜湊值。
2. B伺服器收到該值,並同自己所構建的Merkle Tree的根結點雜湊值對比。
3. 如果兩個值相同,則代表著二者所儲存的檔案完全相同。
4. 否則的話,B伺服器需要像A伺服器索要根結點對應的兩個子結點的雜湊值。
5. A伺服器將對應的值發給B伺服器。
6. B伺服器對比起相應的結點中的雜湊值並重復步驟4和步驟5直到B伺服器找到導致雜湊值不同的一個或幾個資料塊。

顯而易見,在以上演算法中我們僅僅需要傳送雜湊值就可以進行不同伺服器上的資料對比,這一過程是很高效的。並且在對比結束過後我們可以輕易得知哪些資料區塊不同並做相應更新,而非更改整個檔案。

對於p2p系統而言,Merkle Tree主要被用來在peer不一定可信的情況下檢驗從不同的peer所得到的資料區塊是否可信。同上述演算法相反,檢驗資料塊是否可信的演算法是自下而上的。以上面圖中的Merkle Tree為例,如果我們想確保所得到的區塊a是可信的,我們需要利用已有的Hash(b)Hash(c)Hash(d)重新構建整個Merkle Tree直至到達根結點。接著我們可以對比該根雜湊值是否與從一個可靠peer處得到的根雜湊值是否相同來確定所得到區塊a是否可信。或者我們也可以採取像Bitcoin系統一樣假設多數peer可信的原則進行校驗。

複雜度分析

設我們總計有n個數據區塊。

從上面的演算法可以得知,如果要在分散式系統中找到不統一的檔案,我們需要O(logn)時間,因為我們只需沿樹中的某一條路徑一路向下驗證到葉節點即可。而如果要確定p2p系統中我們所得到的資料區塊是否可信,則需要最壞情況下O(n)的時間,因為我們可能需要重構整個Merkle Tree。

Merkle Tree的使用

上面我們已經提到Merkle Tree被廣泛用於分散式系統,p2p系統中。在這些系統中,同一份資料塊往往被儲存於許多不同的地方。因此這樣的系統需要使用Merkle進行高效的資料校驗。

例如Git,其作為一個非常流行的分散式程式碼版本控制系統,就使用Merkle Tree來比較不同機器上分別儲存的程式碼版本是否統一。

Bitcoin,作為當前最流行的匿名分散式加密數字貨幣之一,也採用Merkle Tree來對代表交易的區塊所構成的區塊鏈進行校驗。這些代表交易的區塊鏈分別存在於每一個使用比特幣的使用者的電腦上。每次當由使用者試圖新增一個新的區塊(代表一比新的交易)時,這一改變就將被其他使用者採用基於Merkle Tree的方法來驗證。

Apache Cassandra,最常用的分散式NoSQL資料庫之一,也採用基於Merkle Tree的演算法來確保不同伺服器上的資料備份的一致性。在其所謂Compaction的過程中,Cassandra採用對比Merkle Tree的方式來進行資料一致性的對比,並進行不一致資料塊的更新或修復。