详解rsync算法--如何减少同步文件时的网络传输量
先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A、B和server上的文件内容都是相同的(记为File.1)。某一时刻,B修改了文件内容,上传到SERVER上(记为File.2)。客户端A这时试图向服务器SERVER更新文件到最新内容,也就是File.1更新为File.2。
上面这个场景很常见,例如现在流行的网盘。假设我有一个文件a.txt在网盘上,上班时在公司的单位PC上更新了文件a.txt,下班后回到家里,家里PC硬盘上的a.txt就不是最新的内容,这时网盘就试图从服务器上去拿最新的a.txt了。
那么问题来了,如果在公司电脑上我只是更新了a.txt里很少的一部分内容,例如a.txt共有20M,我只更新了10个字节,难道家里的电脑上,网盘要从服务器上下载20M大小的文件?这明显很浪费带宽。
更有用的场景,假设我的手机android上也用了这个网盘(手机上网费贵得多),只改了几十字节的内容,就要下载20M的文件,得不偿失。或者我把这个文件共享给其他朋友,也有同样的问题:修改少量的内容,却同步完整的文件!
rsync算法就是用来解决上述问题的。client A发送它所保存的旧文件File.1少量的rsync摘要,server拿到后对比本地的File.2内容,得到File.2相对于File.1的变化,然后通过仅发送这个变化来代替发送完整的File.2内容,这样大大减少了网络传输数据。client A收到这个变化后,更新本地的File.1到最新的File.2。就是这么简单。下面详述rsync算法的步骤。
rsync首先需要客户端与服务器之间约定一个块大小,例如1K。然后把File.1等分成多个1K大小的字符串块,每块各计算出MD5摘要和Alder32校验和,如下图。
这里简单介绍下MD5和校验和。MD5是种哈希算法,用于把任意长度的字符串转化为固定为128位的定长字符串,这里可以保证,相同的字符串不可能计算出不同的MD5值。MD5的碰撞率是有的,就是说,两个不同的字符串有可能计算出相同的MD5值,但是这个机率非常小,这里我们忽略不计。例如,在rsync算法里,同一个文件按1K切分成多块,每块都有一个MD5值,如果两块字符串的MD5值相同,则我们认为这两块数据完全相同。
校验和是把上述1K块数据映射为32位大小整型数字上,我们采用Alder32算法,这里同样可以保证,相同的字符串不可能计算出不同的Alder32值。Alder32有两个优点:1、计算非常快,比MD5快多了,成本小;2、当我们有了从0-1024长度的校验和后,计算出1-1025或者2-1026等其他校验和非常方便,只要少量运算即可。当然,它的缺点也很明显,就是碰撞率比MD5高多了,所以,我们要把每个rynsc块同时计算出Alder32校验和与MD5值。Alder32算法我会在本文最后解释。
客户端按1K大小划分File.1文件为许多块,并对每块计算出MD5、Alder32校验和。最后不满1K的数据不做计算。之后,客户端把这些MD5、Alder32校验和依序通过网络传输给服务器,最后不满1K的数据直接发给服务器。那么,服务器收到数据后怎么处理呢?看下图。
首先重申,计算Alder32校验和非常快!
所以,服务器先把最新文件File.2从0字节开始,按1K切分成许多块,每块计算出Alder32校验和,然后与客户端发来的File.1切分出来的Alder32校验和相比,如果alder32值都不一样,毫无疑问,文件内容是不相同的。接着,把File.2从1字节开始,按1K切分成许多块,每块计算出Alder32校验和,再与客户端的校验和比。如此循环下去,直到某个校验和相同了,那么把这段字符串再计算出MD5值,再与客户端过来的对应的MD5值相比(还记得吧?客户端对每个块既计算出Alder32又计算出MD5值),如果不同,则继续往后移1字节,继续比Alder32、MD5值。如果相同,则认为这1K数据,服务器与客户端保存的一致,忽略这块数据(例如1K字节),继续向下看。
全部处理完后,按File.2的文件顺序,向客户端发送以下数据:对于不能够在客户端File.1数据块中找到相同块的字符串,直接列上发出;如果可以找到,则写上MD5和Alder32值,代替原来1024字节的数据块。同样,最后不足1K大小的部分直接列上发出。
纯理论读起来会有些吃力,我再把它简化了举个例子吧。假设客户端与服务器间约定的字符块大小不是1K,而是4个字节。客户端的文件内容是:
taohuiissoman
而服务器的文件内容是:
itaohuiamsoman
现在我们来看看,rsync算法是怎么运作的。
首先,客户端开始分块并计算出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文件结束。
那么,最终客户端与服务器间传输的数据如下图所示。
上面这个例子很简单,可由此推导出复杂的情况,包括File.2对File.1在任意位置上做了增、改、删,都能够完成。
如果这是个大文本文件,应用rsync算法就非常有意义,例如20M的文件,实际可能只传输1M的数据量!这样用户体验会好很多,特别是网速慢的场景。
同时增加的消耗,就是在PC上计算的MD5值和Alder32校验和,这只消耗少量的CPU和内存而已。
最后列下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
D1到Dn就是待计算的字符串块,所有位上的ASC字符。它的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;
- }
详解rsync算法--如何减少同步文件时的网络传输量的更多相关文章
- BM算法 Boyer-Moore高质量实现代码详解与算法详解
Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...
- SVD在推荐系统中的应用详解以及算法推导
SVD在推荐系统中的应用详解以及算法推导 出处http://blog.csdn.net/zhongkejingwang/article/details/43083603 前面文章SVD原理及推 ...
- 一篇看懂JVM底层详解,利用class反编译文件了解文件执行流程
JVM之内存结构详解 JVM内存结构 java虚拟机在执行程序的过程中会将内存划分为不同的区域,具体如图1-1所示. 五个区域 JVM分为五个区域:堆.虚拟机栈.本地方法栈.方法区(元空间).程序计数 ...
- Java虚拟机详解04----GC算法和种类【重要】
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- rsync服务架设(数据同步|文件增量备份)
近期由于业务需要,需要将两台服务器数据保持同步.方案有很多,rsync是其中一种解决方案,本文对rsync的安装及配置进行简单说明,其他实现方式有兴趣可以研究.以下是本文提纲,供参考: rsy ...
- Java虚拟机详解04----GC算法和种类
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- 详解QUiLoader 动态加载.ui文件
http://blog.chinaunix.net/uid-13838881-id-3652523.html 1.适用情况: 动态加载UI文件是指,用 Qt Designer 通过拖拽的方式生产.ui ...
- 《Windows驱动开发技术详解》之IRP的同步
应用程序对设备的同步异步操作: 大部分IRP都是由应用程序的Win32 API函数发起的.这些Win32 API本身就支持同步和异步操作.例如,ReadFile.WriteFile和DeviceIoC ...
- 详解zkw算法解决最小费用流问题
网络流的一些基本概念 很多同学建立过网络流模型做题目, 也学过了各种算法, 但是对于基本的概念反而说不清楚. 虽然不同的模型在具体叫法上可能不相同, 但是不同叫法对应的思想是一致的. 下面的讨论力求规 ...
随机推荐
- UVa 12166 修改天平
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- SSM框架的整合思路&功能实现
这是我第一篇博客,关于SSM框架的整合思路以及简单功能实现. 首先,最近刚刚学习Spring+SpringMVC+Mybatis,在开发时遇到形形色色的问题,周遭人也为我提供了一些思路,我会一点点整理 ...
- OpenGL渲染管线
OpenGL渲染管线具有一系列顺序处理阶段.两个图形信息数据,顶点数据与像素数据,在管线中被处理.组合,最终写入帧缓存.注意,OpenGL可以将处理过的数据送回到你的程序中.(参考灰色区域) Open ...
- Vim的分割窗口split命令
显示两个不同的文件:或者同时显示一个文件的两个不同地方:又或者并排比较两个文件.这一切都可以通过分割窗口实现. 打开新窗口最简单的命令如下: :split (水平分割) || :vsplit (垂直分 ...
- oracle恢复误删除数据
--开启行移动功能alter table 表名 enable row movement;--恢复表数据flashback table 表名 to timestamp to_timestamp('201 ...
- jffs2文件系统制作
内核: linux-3.0 uboot: 2010.09 开发板: fl2440 交叉编译器: 2011. ...
- ubuntu下安装rpm 文件
正想着如何把rpm package 安装到ubuntu上, 发现了这篇文章,转载一下 Ubuntu的软件包格式是deb,如果要安装rpm的包,则要先用alien把rpm转换成deb. sudo a ...
- CSRF攻击
1.什么是CSRF攻击CSRF(Cross-site request forgery),跨站请求伪造.CSRF攻击的原理如下:1)用户登录正常的网站A后,在本地生成Cookie2)在不登出A的情况下, ...
- Angular中的jsonp
1.一般我们使用Angualr中的jsonp值这样使用的:注入$http服务 这样使用jsonp的方式可以支持多数api,但是douban不支持无法使用 module.controller('InTh ...
- PHP网页显示乱码问题总结
其实,乱码,都是因为编码不统一的问题导致的. 以UTF-8编码为例: 第一步,确定数据库字段的编码,字符集:urt8 第二部,PHP脚本设置编码:header('Content-Type: text/ ...