rsync 的核心算法

rsync是unix/linux下同步文件的一个高效算法,它能同步更新两处计算机的文件与目录,并适当利用查找文件中的不一样块以减小数据传输。rsync中一项与其余大部分相似程序或协定中所未见的重要特性是镜像是只对有变动的部分进行传送。rsync可拷贝/显示目录属性,以及拷贝文件,并可选择性的压缩以及递归拷贝。rsync利用由Andrew Tridgell发明的算法。这里不介绍其使用方法,只介绍其核心算法。咱们能够看到,Unix下的东西,一个命令,一个工具都有不少很精妙的东西,怎么学也学不完,这就是Unix的文化啊。html

原本不想写这篇文章的,由于原先发现有不少中文blog都说了这个算法,可是看了一下,发现这些中文blog要么翻译国外文章翻译地很是烂,要么就是介绍这个算法介绍得很乱让人看不懂,还有错误,误人不浅,因此让我以为有必要写篇rsync算法介绍的文章。(固然,我成文比较仓促,可能会有一些错误,请指正)node

问题

首先, 咱们先来想一下rsync要解决的问题,若是咱们要同步的文件只想传不一样的部分,咱们就须要对两边的文件作diff,可是这两个问题在两台不一样的机器上,没法作diff。若是咱们作diff,就要把一个文件传到另外一台机器上作diff,但这样一来,咱们就传了整个文件,这与咱们只想传输不一样部的初衷相背。linux

因而咱们就要想一个办法,让这两边的文件见不到面,但还能知道它们间有什么不一样。这就出现了rsync的算法。算法

算法

rsync的算法以下:(假设咱们同步源文件名为fileSrc,同步目的文件叫fileDstshell

1)分块Checksum算法。首先,咱们会把fileDst的文件平均切分红若干个小块,好比每块512个字节(最后一块会小于这个数),而后对每块计算两个checksum,数组

  • 一个叫rolling checksum,是弱checksum,32位的checksum,其使用的是Mark Adler发明的adler-32算法,数据结构

  • 另外一个是强checksum,128位的,之前用md4,如今用md5 hash算法。ide

为何要这样?由于若干年前的硬件上跑md4的算法太慢了,因此,咱们须要一个快算法来鉴别文件块的不一样,可是弱的adler32算法碰撞几率过高了,因此咱们还要引入强的checksum算法以保证两文件块是相同的。也就是说,弱的checksum是用来区别不一样,而强的是用来确认相同。(checksum的具体公式能够参看这篇文章工具

2)传输算法。同步目标端会把fileDst的一个checksum列表传给同步源,这个列表里包括了三个东西,rolling checksum(32bits)md5 checksume(128bits)文件块编号性能

我估计你猜到了同步源机器拿到了这个列表后,会对fileSrc作一样的checksum,而后和fileDst的checksum作对比,这样就知道哪些文件块改变了。

可是,聪明的你必定会有如下两个疑问:

  • 若是我fileSrc这边在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就彻底和fileDst这边的不同了,但理论上来讲,我应该只须要传一个字符就行了。这个怎么解决?

  • 若是这个checksum列表特别长,而个人两边的相同的文件块可能并非同样的顺序,那就须要查找,线性的查找起来应该特别慢吧。这个怎么解决?

很好,让咱们来看一下同步源端的算法。

3)checksum查找算法。同步源端拿到fileDst的checksum数组后,会把这个数据存到一个hash table中,用rolling checksum作hash,以便得到O(1)时间复杂度的查找性能。这个hash table是16bits的,因此,hash table的尺寸是2的16次方,对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。(对于hash table,若是你不清楚,建议回去看大学时的数据结构教科书)

顺便说一下,我在网上看到不少文章说,“要对rolling checksum作排序”(好比这篇这篇),这两篇文章都引用并翻译了原做者的这篇文章,可是他们都理解错了,不是排序,就只是把fileDst的checksum数据,按rolling checksum作存到2^16的hash table中,固然会发生碰撞,把碰撞的作成一个链表就行了。这就是原文中所说的第二步——搜索有碰撞的状况。

4)比对算法。这是最关键的算法,细节以下:

4.1)取fileSrc的第一个文件块(咱们假设的是512个长度),也就是从fileSrc的第1个字节到第512个字节,取出来后作rolling checksum计算。计算好的值到hash表中查。

4.2)若是查到了,说明发如今fileDst中有潜在相同的文件块,因而就再比较md5的checksum,由于rolling checksume太弱了,可能发生碰撞。因而还要算md5的128bits的checksum,这样一来,咱们就有 2^-(32+128) = 2^-160的几率发生碰撞,这过小了能够忽略。若是rolling checksum和md5 checksum都相同,这说明在fileDst中有相同的块,咱们须要记下这一块在fileDst下的文件编号

4.3)若是fileSrc的rolling checksum 没有在hash table中找到,那就不用算md5 checksum了。表示这一块中有不一样的信息。总之,只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项,那么就会触发算法对fileSrc的rolling动做。因而,算法会住后step 1个字节,取fileSrc中字节2-513的文件块要作checksum,go to (4.1) - 如今你明白什么叫rolling checksum了吧。

4.4)这样,咱们就能够找出fileSrc相邻两次匹配中的那些文本字符,这些就是咱们要往同步目标端传的文件内容了。

图示

怎么,你没看懂? 好吧,我送佛送上西,画个示意图给你看看(对图中的东西我就再也不解释了)。

这样,最终,在同步源这端,咱们的rsync算法可能会获得下面这个样子的一个数据数组,图中,红色块表示在目标端已匹配上,不用传输(注:我专门在其中显示了两块chunk #5,相信你会懂的),而白色的地方就是须要传输的内容(注意:这些白色的块是不定长的),这样,同步源这端把这个数组(白色的就是实际内容,红色的就放一个标号)压缩传到目的端,在目的端的rsync会根据这个表从新生成文件,这样,同步完成。

最后想说一下,对于某些压缩文件使用rsync传输可能会传得更多,由于被压缩后的文件可能会很是的不一样。对此,对于gzip和bzip2这样的命令,记得开启 “rsyncalbe” 模式。

(全文完,转载时请注明做者和出处

(转载本站文章请注明做者和出处 酷 壳 – CoolShell.cn ,请勿用于任何商业用途)

相关文章
相关标签/搜索