3、Rsync算法详解:Rsync工作流程、签名生成、差异计算、数据重组、实际案例分析
Rsync 这个工具,做传输的兄弟应该都用过。但很多人只是「cp 的升级版」这么理解,其实它的核心算法非常巧妙。我当年第一次看 Rsync 源码时,心里就一个想法:这玩意儿的设计者真是个天才。
说白了,Rsync 算法解决的是个经典问题:两个机器上有同一个文件的旧版和新版,怎么只传差异部分?而且接收方只有旧版,发送方只有新版,双方不能提前交换整个文件。
嗯,咱们一步步拆开看。
3.1 Rsync 工作流程:四步走
Rsync 的整个传输过程,可以归纳为四个阶段。我习惯把它叫做「切、签、比、拼」。
- 切分(Split):接收方(目标端)把旧文件切成固定大小的块。默认是 700 字节,但实际会根据文件大小动态调整。
- 签名(Sign):对每个块计算两个签名——弱校验(滚动 checksum)和强校验(MD4/MD5)。
- 比对(Diff):发送方(源端)拿着新文件,用同样的块大小去滑动比对。找到匹配的块就发「引用」,没匹配的就发「原始数据」。
- 重组(Reconstruct):接收方根据收到的指令,把旧文件里的块和新数据拼起来,生成完整的新文件。
核心思想:传输的不是文件本身,而是「如何从旧文件构造新文件」的指令集。
我刚开始接触这个流程时,总觉得第三步「滑动比对」很玄乎。你想想看,如果每个块都去全量比对,那计算量不就炸了吗?Rsync 的精妙之处就在于——它用了两级校验来加速。
3.2 签名生成:弱校验与强校验
签名这一步,是 Rsync 性能的关键。它用了两套校验算法,分工明确。
| 校验类型 | 算法 | 长度 | 作用 |
|---|---|---|---|
| 弱校验 | 滚动 Adler-32 | 4 字节 | 快速筛选,排除明显不匹配的块 |
| 强校验 | MD4 / MD5 | 16 字节 | 精确确认,防止哈希碰撞 |
弱校验用的是 Adler-32 算法。它有个特别牛的特性——滚动更新。什么意思呢?
假设你有一个 700 字节的窗口,从位置 0 滑动到位置 1。普通哈希得重新算 700 个字节,但 Adler-32 只需要:
新校验 = 旧校验 - 出去的字节 + 进来的字节
就这么简单。计算量从 O(n) 降到了 O(1)。
我的经验:我曾经在一个日志同步项目里,文件动辄几百 MB。如果用全量哈希,CPU 直接拉满。换成滚动校验后,CPU 占用降了 80% 以上。这就是算法的力量。
强校验为什么还要存在?因为弱校验只有 4 字节,碰撞概率是 1/2^32。对于大文件来说,这个概率并不低。强校验 MD5 有 16 字节,碰撞概率几乎可以忽略。两级校验配合,既快又准。
3.3 差异计算:滑动窗口匹配
这是 Rsync 最精彩的部分。发送方拿到接收方传来的签名列表后,开始在新文件上滑动窗口。
具体过程是这样的:
- 从新文件开头取 700 字节,计算弱校验。
- 在签名列表里查这个弱校验值。
- 如果命中,再计算强校验做二次确认。
- 如果强校验也匹配,说明找到了一个相同的块,记录「块编号 + 偏移量」。
- 如果弱校验没命中,或者强校验不匹配,就把当前字节标记为「差异数据」,窗口向前滑动 1 字节,继续。
你可能会问:为什么不直接滑 700 字节?
因为插入或删除一个字节后,后面的所有块都会错位。滑动 1 字节才能捕捉到这种「偏移」。这就是滚动校验的用武之地——每次只移 1 字节,但计算量几乎不变。
注意:滑动窗口的步长是 1 字节,不是块大小。这是 Rsync 能处理任意插入/删除操作的根本原因。我曾经见过有人自己实现「简化版 Rsync」,步长设成了块大小,结果文件里加一个字符就全崩了。
匹配结果会生成一组指令,格式大致如下:
# 匹配指令
MATCH block_id=5 offset=3500 length=700
# 差异指令
LITERAL offset=4200 length=3 data="abc"
# 最终指令序列示例
LITERAL offset=0 length=12 data="Hello World"
MATCH block_id=1 offset=700 length=700
MATCH block_id=2 offset=1400 length=700
LITERAL offset=2100 length=5 data="Update"
MATCH block_id=3 offset=2100 length=700
3.4 数据重组:拼装新文件
接收方收到指令序列后,开始重组。这个过程很直观:
- 遇到
LITERAL指令,就把后面的原始数据直接写入输出文件。 - 遇到
MATCH指令,就从旧文件的对应位置拷贝 700 字节到输出文件。
重组完成后,新文件就诞生了。整个过程不需要发送方传输完整的新文件,只传了签名和差异数据。
我算过一笔账:一个 100MB 的文件,如果只改了 1MB 内容,Rsync 实际传输量大约在 1.1MB 左右(1MB 差异数据 + 签名开销)。相比直接传 100MB,省了 99%。
3.5 实际案例分析
说个我亲身经历的项目。当时给某电商平台做静态资源增量更新,前端资源包大概 500MB,每天发版 3-4 次。每次改动其实就几个 JS 文件,总共不到 10MB。
一开始他们用的全量更新,CDN 回源带宽经常被打满。后来我引入了 Rsync 算法做差分更新。
改造前后的对比:
| 指标 | 全量更新 | Rsync 差分更新 |
|---|---|---|
| 平均传输量 | 500 MB | 8-15 MB |
| 传输耗时 | 12 秒 | 0.3 秒 |
| CDN 带宽峰值 | 2.5 Gbps | 0.1 Gbps |
| 服务器 CPU 负载 | 5% | 12%(但可接受) |
你看,传输量降了 97%,耗时降了 97.5%。代价是服务器 CPU 多花了 7%,但这点代价跟带宽成本比起来,根本不值一提。
关键结论:Rsync 算法特别适合「大文件、小改动」的场景。如果文件每次都是全量重写,那 Rsync 的优势就不明显了。选型时要看你的业务特征。
嗯,Rsync 算法就讲到这里。它的核心思想——「用计算换带宽」——在后来的很多差分工具里都能看到影子。理解了 Rsync,你就掌握了差分更新的基本功。