如何實現文件增量同步——算法


問題:

如何增量同步文件,例如一個文本文件有10M,分別存放在A,B兩個地方,現在兩個文件是完全一樣的,但是我馬上要在A上對這個文件進行修改,B如何實現自動和A上的文件保持一致,並且網絡的傳輸量最少。

 

應用場景:

這樣的使用場景太多,這里隨便列舉幾個

1.A機器為線上運營的機器,現在需要一台備份的機器B,當A發生宕機的時候,或者硬盤損壞等各種認為非人為原因導致數據不可用時,可以很快從B恢復

2.SVN這樣的應用場景,不需要每次修改都向服務器發送並替換掉一個文件,而是只發送被修改的部分

3.手機客戶端對一個文本修改,如果那個文本有2M,難道我每次更新都需要上傳整個文件嗎?每次2M,傻子才用! 

等等.... 

 

解決方案:

.分而治之

計算機最重要的基本算法思路就是分而治之,在我們眼里,一個文件不是一個文件,而是一堆存儲塊,每個存儲塊可能20Byte大小,至於這個值具體多大,你可以自己設定,這里的20Byte僅提供參考。通過這樣的方法,一個文件被分成了很多個塊,我們只需要比對塊是否相同就可以得出哪個部分做了相應修改。

 

.快速校驗

剛上面提到如何比對文件,當然這里肯定不會把文件的每個塊上傳去比對,那樣做就沒有意義了。快速比對這不禁讓我想起了哈希規則,哈希表可以通過O(1)的復雜度查找某個key,為什么?  因為它通過計算hash值來初步驗證key,一個key的hash值是唯一的。但是僅僅驗證hash值是不可靠的,因為hash值有可能會沖突,所以在驗證完hash值后,我們在進行key的比較來確定要找的值...

通過哈希的思路,我們可以使用類似的方法來實現文件增量同步,把每一個存儲塊,通過MD5計算其值,然后傳遞MD5值到服務器,讓服務器比對MD5來確定有沒有被修改,如若MD5值不相等,則判定這個文件塊有被修改過

為什么是MD5?

1)能夠將任意長度的字符串轉換為128位定長字符串(MD5 16) 

2)MD5能夠保證絕大部分情況下不同的值hash之后其hash值不一樣,哈希沖突比較少

這樣就可以了嗎?

No,MD5的生成需要占用比較長的CPU時間,所以我們需要尋找一種更簡潔的校驗方式,這里選用Alder32 是一個比較通用的解決方案

 

Alder32有兩個優點: 
1、計算非常快,比MD5快多了,成本小;
2、當我們有了從0-k長度的校驗和后,計算出1-k或者2-k等其他校驗和非常方便,只要少量運算即可。(k可以理解為上面的20Byte)

 

當然,它的缺點也很明顯,就是碰撞率比MD5高多了,所以,我們客戶端需要同時計算出Alder32校驗和與MD5值,傳給服務器,而服務器,為了節省CPU時間,第一步只生成Alder32進行校驗,當值相等時,在進行MD5校驗,這樣服務器就節省了很大的開支。

Alder32算法實現:

 
 A =  1  + D1 + D2 + ... + Dn (mod  65521 )
 B = ( 1  + D1) + ( 1  + D1 + D2) + ... + ( 1  + D1 + D2 + ... + Dn) (mod  65521 )
   = n×D1 + (n− 1 )×D2 + (n− 2 )×D3 + ... + Dn + n (mod  65521 )

 Adler- 32 (D) = B ×  65536  + A

C實現版本

const  int  MOD_ADLER =  65521 ;
 
unsigned  long  adler32(unsigned  char  *data,  int  len)  /*  where data is the location of the data in physical memory and 
                                                       len is the length of the data in bytes  */
{
    unsigned  long  a =  1 , b =  0 ;
     int  index;
 
     /*  Process each byte of the data in order  */
     for  (index =  0 ; index < len; ++index)
    {
        a = (a + data[index]) % MOD_ADLER;
        b = (b + a) % MOD_ADLER;
    }
 
     return  (b <<  16 ) | a;
}

 

三.實現更改

因為已經找出來了文件不同的地方,所以只需要按需上傳更改的部分到服務器,然后服務器做更改就可以了。 

 

 

實例分析: 

理論概述完畢,來點小例子子

客戶端文件內容是: 

 taohuiissoman

而服務器的文件內容是:

itaohuiamsoman

 

首先,客戶端開始分塊並計算出MD5和Alder32值。

如上圖,像taoh是一塊,對taoh分別計算出MD5和alder32值。以此類推,最后一個n字母不足4位保留。於是,客戶端把計算出的MD5和alder32按順序發出,最后發出字符n。

 

服務器收到后,先把自己保存的File.2的內容按4字節划分。

划分出itao、huia、msom、an,當然,這些串的Alder32值肯定無法從File.1里划分出的:taoh、uiis、soma、n找出相同的。於是向后移一個字節,從t開始繼續按4字節划分。

從taoh上找到了alder32相同的塊,接着再比較MD5值,也相同!於是記下來,跳過taoh這4個字符,看uiam,又找不到File.1上相同的塊了。繼續向后跳1個字節從i開始看。還是沒有找到Alder32相同,繼續向后移,以此類推。

到了soma,又找到相同的塊了。

 

重復上面的步驟,直到File.2文件結束。

 

通過這個簡單的例子,可以設想一下其他任何的增刪查改功能 

 

 

參考資料:http://cs.anu.edu.au/techreports/1996/TR-CS-96-05.pdf 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM