rsync技術報告(翻譯),rsync技術報告翻譯
本篇為rsync官方推薦技術報告rsync technical report的翻譯,主要內容是Rsync的演算法原理以及rsync實現這些原理的方法。翻譯過程中,在某些不易理解的地方加上了譯者本人的注釋。
本文目錄:
1.1 摘要
1.2 The problem
1.3 The rsync algorithm
1.4 Rolling checksum
1.5 Checksum searching
1.6 Pipelining
1.7 Results
Rsync
演算法
Andrew Tridgell Paul Mackerras
Department of Computer Science
Australian National University
Canberra, ACT 0200, Australia
1.1 摘要
本報告介紹了一種將一台機器上的檔案更新到和另一台機器上的檔案保持一致的演算法。我們假定兩台機器之間通過低頻寬、高延遲的雙向鏈路進行通訊。該演算法計算出源檔案中和目標檔案中一致的部分(譯者註:資料區塊一致的部分),然後僅發送那些無法匹配(譯者註:即兩端檔案中不一致的部分)的部分。實際上,該演算法計算出了兩個機器上兩檔案之間一系列的不同之處。如果兩檔案相似,該演算法的工作效率非常高,但即使兩檔案差別非常大,也能保證正確且有一定效率的工作。
1.2 The problem
假設你有兩個檔案A和B,你希望更新B讓它和A完全相同,最直接的方法是拷貝A變為B。
但想象一下,如果這兩個檔案所在機器之間以極慢的通訊鏈路進行串連通訊,例如使用撥號的IP鏈路。如果A檔案很大,拷貝A到B速度是非常慢的。為了提高速度,你可以將A壓縮後發送,但這種方法一般只能獲得20%到40%的提升。
現在假定A和B檔案非常相似,也許它們兩者都是從同一個源檔案中分離出來的。為了真正的提高速度,你需要利用這種相似性。一個通用的方法是通過鏈路僅發送A和B之間差異的部分,然後根據差異列表重組檔案(譯者註:所以還需要建立差異列表,發送差異列表,最後匹配差異列表並重組)。
這種通用方法的問題是,要建立兩個檔案之間的差異集合,它依賴於有開啟並讀取兩檔案的能力。因此,這種方法在讀取兩檔案之前,要求事先從鏈路的另一端擷取檔案。如果無法從另一端擷取檔案,這種演算法就會失效(但如果真的從另一端擷取了檔案,兩檔案將同在一台機器上,此時直接拷貝即可,沒有必要比較這些檔案的差異)。rsync就是處理這種問題的。
rsync演算法能高效率地計算出源檔案和目標已存在檔案相同的部分(譯者註:即能匹配出相同的資料區塊)。這些相同的部分不需要通過鏈路發送出去;所有需要做的是引用目標檔案中這些相同的部分來做匹配參照物(譯者註:即基準檔案basis file)。只有源檔案中不能匹配上的部分才會以純資料的方式被逐位元組發送出去。之後,接收端就可以通過這些已存在的相同部分和接收過來的逐位元組資料群組建成一個源檔案的副本。
一般來說,發送到接收端的資料可以使用任意一種常見的壓縮演算法進行壓縮後傳輸,以進一步提高速度。
1.3 The rsync algorithm
假設我們有兩台電腦α和β,α上有可以訪問的檔案A,β上有可以訪問的檔案B,且A和B兩檔案是相似的。α和β之間以低速鏈路通訊。
rsync演算法由以下過程組成:
1.β將檔案B分割為一系列不重疊且大小固定為S位元組(譯者註:作者們備忘說500到1000位元組比較適合)的資料區塊。當然,最後一個資料區塊可能小於S位元組。
2.β對每個這樣的資料區塊都計算出兩個校正碼:32位的弱校正碼rolling-checksum和128位的強校正碼MD4-checksum(譯者註:現在的rsync使用的是128位的MD5-checksum)。
3.β將這些校正碼發送給α。
4.α將搜尋檔案A,從中尋找出所有長度為S位元組且和B中兩個校正碼相同的資料區塊(從任意位移量搜尋)。這個搜尋和比較的過程可以通過使用弱滾動校正(rolling checksum)的特殊功能非常快速地完成。
(譯者註:以字串123456為例,要搜尋出包含3個字元的字串,如果以任意位移量的方式搜尋所有3個字元長度的字串,最基本方法是從1開始搜尋得到123,從2開始搜尋得到234,從3開始搜尋得到345,直到搜尋完成。這就是任意位移量的意思,即從任意位置搜尋長度為S的資料區塊)
(譯者再註:之所以要以任意位移量搜尋,考慮一種情況,現有兩個完全相同的檔案A和B,現在向A檔案的中間插入一段資料,那麼A中從這段資料開始,緊跟其後的所有資料區塊的位移量都向後挪動了一段長度,如果不以任意位移量搜尋固定長度資料區塊,那麼從新插入的這段資料開始,所有的資料區塊都和B不一樣,在rsync中意味著這些資料區塊都要傳輸,但實際上A和B不同的資料區塊只有插入在中間的那一段而已)
5.α將一系列的指令發送給β以使其構造出A檔案的副本。每個指令要麼是對B中資料區塊的引用,要麼是純資料。這些純資料是A中無法匹配到B中資料區塊的資料區塊部分(譯者註:就是A中不同於B的資料區塊)。
最終的結果是β擷取到了A的副本,但卻僅發送了A中存在B中不存在的資料部分(還包括一些校正碼以及資料區塊索引資料)。該演算法也僅只要求一次鏈路往返(譯者註:第一次鏈路通訊是β發送校正碼給α,第二次通訊是α發送指令、校正碼、索引和A中存在B中不存在的純資料給β),這可以最小化鏈路延遲導致的影響。
該演算法最重要的細節是滾動校正(rolling checksum)以及與之相關的多備選(multi-alternate)搜尋機制,它們保證了所有位移校正(all-offsets checksum,即上文步驟4中從任意位移位置搜尋資料區塊)的搜尋過程可以處理的非常迅速。下文將更詳細地討論它們。
(譯者註:如果不想看演算法理論,下面的演算法具體內容可以跳過不看(可以看下最後的結論),只要搞懂上面rsync演算法過程中做了什麼事就夠了。)
1.4 Rolling checksum
rsync演算法使用的弱滾動校正(rolling checksum)需要能夠快速、低消耗地根據給定的緩衝區X1 ...Xn 的校正值以及X1、Xn+1的位元組值計算出緩衝區的校正值。
我們在rsync中使用的弱校正演算法設計構想來自Mark Adler的adler-32校正。我們的校正定義公式為:
其中s(k,l)是位元組的滾動校正碼。為了簡單快速地計算出滾動校正碼,我們使用。
此校正演算法最重要的特性是能使用遞迴關係非常高效地計算出連續的值。
因此可以為檔案中任意位移位置的S長度的資料區塊計算出校正碼,且計算次數非常少。
儘管該演算法足夠簡單,但它已經足夠作為兩個檔案資料區塊匹配時的第一層檢查。我們在實踐中發現,當資料區塊內容不同時,校正碼(rolling checksum)能匹配上的機率是很低的。這一點非常重要,因為每個弱校正能匹配上的資料區塊都必須去計算強校正碼,而計算強校正碼是非常昂貴的。(譯者註:也就是說,資料區塊內容不同,弱校正碼還是可能會相同(儘管機率很低),也就是rolling checksum出現了碰撞,所以還要對弱校正碼相同的資料區塊計算強校正碼以做進一步匹配)
1.5 Checksum searching
當α收到了B檔案資料區塊的校正碼列表,它必須要去搜尋A檔案的資料區塊(以任意位移量搜尋),目的是找出能匹配B資料區塊校正碼的資料區塊部分。基本的策略是從A檔案的每個位元組開始依次計算長度為S位元組的資料區塊的32位弱滾動校正碼(rolling checksum),然後對每一個計算出來的弱校正碼,都拿去和B檔案校正碼列表中的校正碼進行匹配。在我們實現的演算法上,使用了簡單的3層搜尋檢查(譯者註:3步搜尋過程)。
第一層檢查,對每個32位弱滾動校正碼都計算出一個16位長度的hash值,並將每216個這樣的hash條目組成一張hash表。根據這個16位hash值對校正碼列表(例如接收到的B檔案資料區塊的校正碼集合)進行排序。hash表中的每一項都指向校正碼列表中對應hash值的第一個元素(譯者註:即資料區塊ID),或者當校正碼列表中沒有對應的hash值時,此hash表項將是一個空值(譯者註:之所以有空校正碼以及對應空hash值出現的可能,是因為如果rsync命令列中指定了"--whole-file"選項時,β會將那些α上有而β上沒有的檔案的校正碼設定為空白並一同發送給α,這樣α在搜尋檔案時就會知道β上沒有該檔案,然後直接將此檔案整個發送給β)。
對檔案中的每個位移量,都會計算它的32位滾動校正碼和它的16位hash值。如果該hash值的hash表項是一個非空值,將調用第二層檢查。
(譯者註:也就是說,第一層檢查是比較匹配16位的hash值,能匹配上則認為該資料區塊有潛在相同的可能,然後從此資料區塊的位置處進入第二層檢查)
第二層檢查會對已排序的校正碼列表進行掃描,它將從hash表項指向的條目處(譯者註:此條目對應第一層檢查結束後能匹配的資料區塊)開始掃描,目的是尋找出能匹配當前值的32位滾動校正碼。當掃描到相同32位弱滾動校正值時或者直到出現不同16位hash值都沒有匹配的32位弱校正碼時,掃描終止(譯者註:由於hash值和弱校正碼重複的機率很低,所以基本上向下再掃描1項最多2項就能發現無法匹配的弱滾動校正碼)。如果搜尋到了能匹配的結果,則調用第三層檢查。
(譯者註:也就是說,第二層檢查是比較匹配32位的弱滾動校正碼,能匹配上則表示還是有潛在相同的可能性,然後從此位置處開始進入第三層檢查,若沒有匹配的弱滾動校正碼,則說明不同資料區塊內容的hash值出現了重複,但好在弱滾動校正的匹配將其排除掉了)
第三層檢查會對檔案中當前位移量的資料區塊計算強校正碼,並將其與校正碼列表中的強校正碼進行比較。如果兩個強校正碼能匹配上,我們認為A中的資料區塊和B中的資料區塊完全相同。理論上,這些資料區塊還是有可能會不同,但是機率是極其微小的,因此在實踐過程中,我們認為這是一個合理的假設。
當發現了能匹配上的資料區塊,α會將A檔案中此資料區塊的位移量和前一個匹配資料區塊的結束位移地址發送給β,還會發送這段匹配資料區塊在B中資料區塊的索引(譯者註:即資料區塊的ID,chunk號碼)。當發現能匹配的資料時,這些資料(譯者註:包括匹配上的資料區塊相關的重組指令以及處於兩個匹配塊中間未被匹配的資料區塊的純資料)會立即被發送,這使得我們可以將通訊與進一步的計算並存執行。
如果發現檔案中當前位移量的資料區塊沒有匹配上時,弱校正碼將向下滾動到下一個位移地址並且繼續開始搜尋(譯者註:也就是說向下滾動了一個位元組)。如果發現能匹配的值時,弱校正碼搜尋將從匹配到的資料區塊的終止位移地址重新開始(譯者註:也就是說向下滾動了一個資料區塊)。對於兩個幾乎一致的檔案(這是最常見的情況),這種策略節省了大量的計算量。另外,對於最常見的情況,A的一部分資料能依次匹配上B的一系列資料區塊,這時對資料區塊索引號進行編碼將是一件很簡單的事。
1.6 Pipelining
上面幾個小章節描述了在遠程系統上組建一個檔案副本的過程。如果我們要拷貝一系列檔案,我們可以將過程流水線化(pipelining the process)以期獲得很可觀的延遲上的優勢。
這要求β上啟動的兩個獨立的進程。其中一個進程負責產生和發送校正碼給α,另一個進程則負責從α接收不同的資訊資料以便重組檔案副本。(譯者註:即generator-->sender-->receiver)
如果鏈路上的通訊是被緩衝的,兩個進程可以相互獨立地不斷向前工作,並且大多數時間內,可以保持鏈路在雙方向上被充分利用。
1.7 Results
為了測試該演算法,建立了兩個不同Linux核心版本的源碼檔案的tar包。這兩個核心版本分別是1.99.10和2.0.0。這個tar包大約有24M且5個不同版本的補丁分隔。
在1.99.10版本中的2441個檔案中,2.0.0版本中對其中的291個檔案做了更改,並刪除了19個檔案,以及添加了25個新檔案。
使用標準GNU diff程式對這兩個tar包進行"diff"操作,結果產生了超過32000行總共2.1 MB的輸出。
下表顯示了兩個檔案間使用不同資料區塊大小的rsync的結果。
block size |
matches |
tag hits |
false alarms |
data |
written |
read |
300 |
64247 |
3817434 |
948 |
5312200 |
5629158 |
1632284 |
500 |
46989 |
620013 |
64 |
1091900 |
1283906 |
979384 |
700 |
33255 |
571970 |
22 |
1307800 |
1444346 |
699564 |
900 |
25686 |
525058 |
24 |
1469500 |
1575438 |
544124 |
1100 |
20848 |
496844 |
21 |
1654500 |
1740838 |
445204 |
在每種情況下,所佔用的CPU時間都比在兩個檔案間直接運行"diff"所需時間少。
表中各列的意思是:
block size:計算校正和的資料區塊大小,單位位元組。
matches:從A中匹配出B中的某個資料區塊所匹配的次數。(譯者註:相比於下面的false matches,這個是true matches)
tag hits:A檔案中的16位hash值能匹配到B的hash表項所需的匹配次數。
false alarms:32位弱滾動校正碼能匹配但強校正碼不能匹配的匹配次數。(譯者註:即不同資料區塊的rolling checksum出現小機率假重複的次數)
data:逐位元組傳輸的檔案純資料,單位位元組。
written:α所寫入的總位元組數,包括協議開銷。這幾乎全是檔案中的資料。
read:α所讀取的總位元組數,包括協議開銷,這幾乎全是校正碼資訊資料。
結果表明,當塊大小大於300位元組時,僅傳輸了檔案的一小部分(大約5%)資料。而且,相比使用diff/patch方法更新遠端檔案,rsync所傳輸的資料也要少很多。
每個校正碼對佔用20個位元組:弱滾動校正碼佔用4位元組,128位的MD4校正碼佔用16位元組。因此,那些校正碼本身也佔用了不少空間,儘管相比於傳輸的純資料大小而言,它們要小的多。
false alarms少於true matches次數的1/1000,這說明32位滾動校正碼可以很好地檢測出不同資料區塊。
tag hits的數值表明第二層校正碼搜尋檢查演算法大致每50個字元被調用一次(譯者註:以block size=1100那行資料為例,50W*50/1024/1024=23M)。這已經非常高了,因為在這個檔案中所有資料區塊的數量是非常大的,因此hash表也是非常大的。檔案越小,我們期望tag hit的比例越接近於匹配次數。對於極端情況的超大檔案,我們應該合理地增大hash表的大小。
下一張表顯示了更小檔案的結果。這種情況下,眾多檔案並沒有被歸檔到一個tar包中,而是通過指定選項使得rsync向下遞迴整個分類樹。這些檔案是以另一個稱為Samba的軟體包為源抽取出來的兩個版本,源碼大小為1.7M,對這兩個版本的檔案運行diff程式,結果輸出4155行共120kB大小。
block size |
matches |
tag hits |
false alarms |
data |
written |
read |
300 |
3727 |
3899 |
0 |
129775 |
153999 |
83948 |
500 |
2158 |
2325 |
0 |
171574 |
189330 |
50908 |
700 |
1517 |
1649 |
0 |
195024 |
210144 |
36828 |
900 |
1156 |
1281 |
0 |
222847 |
236471 |
29048 |
1100 |
921 |
1049 |
0 |
250073 |
262725 |
23988 |
回到系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7220753.html