Rsync算法
概述
这篇文章主要讨论在另外一台机器上更新同一文件的算法。我们假设这两台机器是处在一个高延迟低带宽的互联状况下。这个算法会识别出源文件和目的文件有一部分是相同的,并且只传出那些不同的部分。实际上,这个算法是在两个文件处于的不同的机器上计算出他们的不同。该算法不仅可以在在两个文件及其相似的情况下有效的工作,在两个文件很大不同的情况下也可以工作的很好。
Rsync算法
假设我们现在有两台机器A和B,A中包含一个文件M,B中包含一个文件N,当M和N“相似”的时候,我们在这两台机器建立一个连接。假设这个连接是低带宽高延迟的,比较差的连接。
Rsync由以下步骤组成:
1:B机器将文件N分成一系列固定的大小且相互不重叠的数据块,这些数据块的大小假设为S。一般情况下,S的取值范围为500~1000字节。最后一个数据块当然小于S了。
2:为每一个数据块计算出两个校验码:一个弱递归的32bit校验码和一个强的128bit校验码。
3:机器B会将这些校验码发送给机器A。
4:当机器A收到这些校验码之后,会在文件M中以S为单位寻找相同的两个校验码(并不一定是S的倍数,可以有任何情况的补偿)。这个工作可在使用下面讲到的递归算法的情况下以一个单一且单向的方式非常快的完成。
5机器A发送给B一个连续的重构指令来重构文件M。每一个指令都是根据N的数据块或是逐字节生成的。逐字节的数据只是在当A中没有找到相同的数据块的时候才向B发送。
这样做的结果是在B机器上有了一份文件M的复制。但是仅仅是那些在在N中没有的M的部分(加上一部分校验码和块索引的部分)被传送了。这个算法仅仅需要一次往返,因此对延迟的需求也变得很小。
最重要的细节部分是关于递归校验码和如何让校验码的查找非常快的完成。这些将在下面进行讨论。
递归校验码
这个用于rsync算法的弱递归校验码必须拥有一个低消耗的校验算法来实现在拥有X1 .. Xn和X1 、 Xn+1的情况下得到X2 .. Xn+1。
我们使用的这个弱校验码是由Mark Adler’s adler-32 checksum创造出来的。这个校验码是这样定义的。
s(k,l) = a(k,l) + 216 b(k,l)
这里的s(k,l)是一个关于Xi。。。Xl的递归校验码。为了加速和简单化,我们将M的值设为216。
这个校验码最重要的指标就是该连续值可以下列公式非常高效的计算出来:
这样,这个校验码就会根据S长度的数据块通过“递归”的方式,通过较小的计算代价计算出来。
尽管很简单,这个校验码还不能完全胜任匹配两个文件的数据块。我们在实践中会发现可能两个校验码匹配的数据块实际上并不完全一样。这就是为什么我们还是用了开销巨大的强校验码来保证两个数据块的匹配。
校验码查找
当机器A收到机器B发给他的关于数据块的校验码后,它必须在文件M中查找所有与B匹配的数据块的校验码。这个基本策略将为文件M计算一系列32 bit的校验码,对每一个校验码,都会去寻找与B相匹配。这个将分成3级来执行。
第1级:在32bit的递归校验码中使用一个16bit的hash和一个216条目的hash表。校验码的值列表(就是从B发送过来的那个)根据这个16bit的hash来进行整理。每一个条目都指向这个列表的元素。如果没有元素就说明这个hash表没有元素。
文件中的每个32bit递归校验码和他的16bit hash都会被计算。如果hash表中的hash值不是个空值,那么第二级将被触发。
第二级:
第二级检查包括根据hash表中的条目指向的元素来扫描排序后的hash列表。搜索哪一个32bit递归校验码的值匹配当期值。这个检查直到当16bit的hash元素不同的时候。如果找到了匹配,将会出发第三级检查。
第三级:
第三级检查将为当前数集触发强校验码的计算并且比较两个强校验码。如果两个强校验码也匹配,那么我们就认为我们在文件M中找到了个和N中完全相同的数据块。但是这两个还是有可能不同的,但是这种情况已经是微乎其微了,并且在实际中我们认为这个是可信任的。
一旦一个匹配被发现了,机器A将发送当前文件在B上匹配成功的数据块的索引。这些信息将立即被找到,并且让我们可以继续对重叠部分进行更进一步的计算。
如果在文件中匹配不成功,这个校验码就会被更新,然后继续。如果匹配被找到,会在匹配的数据块的末尾重新开始。这个策略用来解决两个文件非常近似的情况下计算的总量。为每个数据块索引编码是简单的,通常情况下文件M匹配的时候也是顺寻的。
流水线
Rsync通过流水线的方式来进行处理。并且可以启用多个独立的进程来实现。