rsync核心演算法介紹及應用探索

2021-07-24 17:22:44 字數 3635 閱讀 5467

rsync是unix/linux下同步檔案的乙個高效演算法,它能同步更新兩台計算機的檔案與目錄,並查詢檔案中的不同塊以減少資料傳輸。rsync的乙個重要特性就是只對變更部分進行傳送。rsync可拷貝、顯示目錄屬性以及拷貝檔案,並可選擇性的壓縮以及遞迴拷貝。rsync是由andrew tridgell發明的。

rsync要解決差異化同步,前提就必須知道原始檔和目標檔案之間的差異,特別是當原始檔與目標檔案分別位於兩台計算機上時。

rsync的演算法流程:

1) 分塊checksum演算法:將目標檔案按指定塊大小平均分成若干塊,然後對每塊計算兩個checksum值。涉及的兩個演算法如下:

a) rolling checksum,32位,採用adler-32演算法;比較弱,碰撞機率高;

b) md5 hash,128位,強checksum,碰撞機率低;

顯然,如果rolling checksum 值不同,那麼兩個檔案塊肯定不同;但是,如果rolling checksum 值相同,但因其高碰撞率我們無法確定兩個檔案塊就是相同的,這時就需要進一步比較md5的值了,畢竟md5的碰撞機率達到2* 因此,可以說:adler-32用來區別不同,而md5用來進一步確認相同。

2) 目標檔案的checksum計算完成後,接下來同步目標端就需要將這個checksum列表告訴給原始檔端,給其提供乙個差異檢查的參照物。這個列表的每一項都包含以下三個資訊:rolling checksum、md5、檔案塊號。同步源端拿到這個列表後,對原始檔進行同樣的checksum,然後對比,就可以知道哪些檔案塊發生了變化。僅僅如此簡單嗎?我們接著看下面這兩個問題:

a) 如果在原始檔中間插入了乙個字元,其後的檔案塊都將偏移乙個字元,顯然和目標檔案已經不同了,checksum資訊也發生了改變。但從理論上將,我們應該只傳輸乙個字元到目標端。怎麼解決呢?

b) 如果目標檔案很大,對應的checksum列表也將很長,在對原始檔進行checksum比較時採用線性查詢,顯然比較慢。又該怎麼辦?

3) 查詢演算法。同步源端拿到目標檔案的checksum列表後,將之存到乙個hash table中,用rolling checksum做hash-key,以便獲得o(1)時間複雜度的查詢效能。這個hash table是16bits的,所以,hash table的大小是2^16,對rolling checksum的hash會被雜湊到0 到 2^16 – 1中的某個整數值。顯然,hash表中的key會發生碰撞,我們只需要將碰撞的checksum做成乙個鍊錶就可以了。

4) 比對演算法。這是最關鍵的演算法,細節如下:

a) 取原始檔的第乙個檔案塊(長度設為n),即從原始檔的第1個位元組到第n個位元組計算rolling checksum,然後到hash表中查詢;

b) 如果查到了,表明原始檔與目標檔案中有潛在相同的檔案塊;繼續計算md5並比較。如果rolling checksum和md5都相同,可確定相同檔案塊存在,記下檔案塊號。(兩次checksum比較的碰撞概率為2^-(32+128) = 2^-160,可以忽略);

c) 如果沒查到,不用計算md5,就可以確定兩個檔案塊不同。後移 1個位元組,取原始檔中的2-n+1位元組的檔案塊繼續執行步驟a);

最終,在同步源端,rsync演算法可能會得到下圖所示的乙個資料陣列。圖中,紅色塊表示在目標端已匹配上,不用傳輸,而白色的地方就是需要傳輸的內容(注意:白色塊是不定長的,紅色塊僅僅是乙個檔案塊編號,由目標端提供的)。當目標端拿到這個陣列後,就可以重新生成乙個新的檔案了。

演算法到這裡就介紹完了,為了提高效率,由兩個地方值得考慮:

a) 計算checksum時涉及檔案i/o操作,檔案塊的大小將決定io效率,建議去檔案系統的最小分配單元。比如,fat系統下取簇大小。

b) 源與目標兩端進行資料傳輸時都可考慮使用壓縮演算法,節省網路頻寬,畢竟計算機的執行速度越來越快,不再會成為檔案同步的瓶頸。

假設一段資料長度為n,每個位元組依次為d1、d2 、...、dn,那麼adler-32的計算公式如下:
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的**:

uint32  adler32(const

uchar *buffer, uint32 length)

return (b << 16) | a;

}

每計算乙個位元組都做一次取模運算似乎沒必要吧!兩個位元組相加怎麼也不可能超過65521啊!於是,進一步優化**,如下:

#define nmax 5552

while (--n);

a %= mod_adler32;

b %= mod_adler32;

}/* do remaining bytes (less than nmax, still just one modulo) */

if (length)

a %= mod_adler32;

b %= mod_adler32;

}/* return recombined sums */

return a | (b << 16);}

nmax宣告為5552有什麼依據呢?

來看一下公式中的b = n×d1 + (n-1)×d2 + (n-2)×d3 + … + dn + n (mod 65521)。最壞情況下每個位元組都為255,那麼必須b = 255×n×(n+1)/2 + (n+1) ×(65521-1) <= 2^32-1,以保證b(32位無符號整形)不越界。

另外,在rsync中每步進乙個位元組有沒有必要重新對整個檔案塊n個位元組計算一次adler-32值?

還是看一下公式:

a  = 1 + d1 + d2 + ... + dn     

a』 = 1 + d2 + d3 + ... + dn + dn+1

= a –d1 + dn+1

b = n×d1 + (n-1)×d2 + (n-2)×d3 + ... + dn + n

b』= n×d2 + (n-1)×d3 + ... + 2×dn + 1×dn+1 + n

= b - n×d1 + d2 + d3 +... + dn + dn+1

= b - n×d1 + a』- 1

如此以來,每步進乙個位元組只需操作兩個位元組的資料,大大減少了運算量。

rsync.lib實現adler-32、md5、hash search等核心演算法;

zip.lib實現資料壓縮和解壓縮,可直接借鑑開源庫;

network.lib實現網路傳輸部分,保證資料完整性。

rsync 的核心演算法

rsync是unix linux下同步檔案的乙個高效演算法,它能同步更新兩處計算機的檔案與目錄,並適當利用查詢檔案中的不同塊以減少資料傳輸。rsync中一項與其他大部分類似程式或協定中所未見的重要特性是映象是只對有變更的部分進行傳送。rsync可拷貝 顯示目錄屬性,以及拷貝檔案,並可選擇性的壓縮以及...

rsync 的核心演算法

rsync是unix linux下同步檔案的乙個高效演算法,它能同步更新兩處計算機的檔案與目錄,並適當利用查詢檔案中的不同塊以減少資料傳輸。rsync中一項與其他大部分類似程式或協定中所未見的重要特性是映象是只對有變更的部分進行傳送。rsync可拷貝 顯示目錄屬性,以及拷貝檔案,並可選擇性的壓縮以及...

rsync的核心演算法

rsync是unix linux下同步檔案的乙個高效演算法,它能同步更新兩處計算機的檔案與目錄,並適當利用查詢檔案中的不同塊以減少資料傳輸。rsync中一項與其他大部分類似程式或協定中所未見的重要特性是映象是只對有變更的部分進行傳送。rsync可拷貝 顯示目錄屬性,以及拷貝檔案,並可選擇性的壓縮以及...