rsync结合--link-dest与--hard-links备份时处理硬链接目录结构异常的问题咨询
我自己搭了一套基于rsync的夜间增量备份方案,用--link-dest只同步新增/变更的内容,再加--hard-links来忠实还原源目录里的硬链接结构——这两个参数配合起来既能省网络流量,又能少占存储空间,一直用着都挺顺的。
最近我想整理"media"目录下几百个分散在各子目录里的媒体文件,想着最省空间的办法应该是先建一个平行的目录结构,用硬链接指向原文件,然后在新目录里随意移动这些硬链接(本来以为除了目录条目不会额外占空间),最后把新的目录结构替换掉原来的"media"目录就行。
结果出乎意料:这个操作触发了rsync大量复制文件、传输数据的行为。我用--dry-run仔细排查后发现,rsync的处理逻辑有点反直觉:它会先把新目录里的文件同步到备份里,然后再给旧目录里的对应文件创建硬链接(相当于丢弃之前备份里的旧文件副本),而不是反过来——直接给新目录里的文件创建指向旧备份中已有文件的硬链接。
我观察到的规律
看起来rsync是按字母顺序遍历源目录,再去对比上一次的备份内容。对于inode相同的文件(也就是硬链接),rsync先处理到的那个会决定是链接到旧备份还是重新复制,后面遇到的同inode文件则会链接到这个第一个被处理文件的备份副本。如果先碰到新目录里的文件,就会触发复制;如果先碰到旧目录里的文件,就会直接链接到旧备份里的已有文件。
我想请教的两个问题
- 除了手动调整目录名称让排序符合预期之外,有没有其他办法能解决这个问题?(我觉得rsync其实可以先扫描所有同inode的源文件,检查其中是否有任何一个在目标备份里有对应项,而不是只看第一个被遍历到的文件)
- 有没有办法不用手动修改备份里的目录名称?我原本的需求是把MyDir下的文件移到不同子目录,所以建了MyDir2,在里面创建指向MyDir文件的硬链接并整理结构,现在整理完了想让新结构用回MyDir这个名字。但直接修改MyDir会触发文件复制,目前我找到的临时办法是:在源目录和最后一次备份里都删掉MyDir,再把MyDir2改名为MyDir,但总觉得有点麻烦。
测试中的困惑
后来我用小例子复现这个场景,结果却完全符合预期——不管遍历顺序如何,源里新增的硬链接在目标备份里都会生成对应的硬链接,没有出现复制文件的情况。这就让我更懵了:难道是备份的大小影响了rsync的行为?我实际要处理的备份是3TB规模,包含80万个文件、15万个目录,而测试的小例子只有几个文件。
测试的具体操作与输出
我做的测试步骤是这样的:
初始源目录结构:
source └── dir2 ├── file1 └── file2
先把source同步到dest1:rsync --archive source/ dest1/
然后在source里新建dir1和dir3,里面分别创建指向file1和file2的硬链接:
source ├── dir1 │ └── file1 ├── dir2 │ ├── file1 │ └── file2 └── dir3 └── file2
接着运行备份命令:
rsync --archive --link-dest ../dest1 --hard-links --itemize-changes source/ dest2/
输出结果是:
created directory dest2 cd..t...... ./ cd+++++++++ dir1/ hf+++++++++ dir1/file1 => dir2/file1 cd+++++++++ dir3/ hf+++++++++ dir3/file2 => dir2/file2
和实际备份的情况完全不同,dest2里没有生成新文件,只有硬链接。
备注:内容来源于stack exchange,提问作者ciobi

