(轉)文件增量同步算法實現

ref https://www.xuebuyuan.com/3203628.htmlhtml


 

問題:算法

如何增量同步文件,例如一個文本文件有10M,分別存放在A,B兩個地方,如今兩個文件是徹底同樣的,可是我立刻要在A上對這個文件進行修改,B如何實現自動和A上的文件保持一致,而且網絡的傳輸量最少。服務器

 

應用場景:網絡

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

1.A機器爲線上運營的機器,如今須要一臺備份的機器B,當A發生宕機的時候,或者硬盤損壞等各類認爲非人爲緣由致使數據不可用時,能夠很快從B恢復ui

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

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

等等.... code

 

解決方案:orm

.分而治之

計算機最重要的基本算法思路就是分而治之,在咱們眼裏,一個文件不是一個文件,而是一堆存儲塊,每一個存儲塊可能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有兩個優勢: 
一、計算很是快,比MD5快多了,成本小;
二、當咱們有了從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 

相關文章
相關標籤/搜索