强制rsync按字节比较本地文件而不是校验和。

6

我编写了一个Bash脚本来备份一个文件夹。脚本的核心是一个rsync指令。

rsync -abh --checksum /path/to/source /path/to/target

我正在使用--checksum,因为我不想依赖文件大小或修改时间来确定源路径中的文件是否需要备份。然而,大多数情况下,我在本地运行此脚本,即连接了外部USB驱动器的备份目标文件夹;没有通过网络进行备份。因此,在同一台机器上完全读取和处理两个文件,不存在增量传输的必要。在这种情况下,计算校验和甚至会导致速度变慢。如果rsync只是对存储在本地的文件进行diff,那就更好了。
阅读手册后,我发现了--whole-file选项,它似乎避免了昂贵的校验和计算。手册还指出,如果源路径和目标路径都是本地路径,则这是默认设置。
因此,我考虑将我的rsync语句更改为:
rsync -abh /path/to/source /path/to/target
rsync现在会逐字节检查本地源和目标文件,还是使用修改时间和/或大小来确定是否需要备份源文件?我绝对不希望仅仅依靠文件大小或修改时间来决定是否进行备份。

更新

请注意rsync指令中的-b选项。这意味着目标文件将在被替换之前被备份。因此,直接忽略源文件夹中所有文件的同步,例如采用评论中建议的--ignore-times,是不可行的。这将创建太多重复文件并浪费存储空间。同时请记住,我试图减少本地机器的备份时间和工作量。只是简单备份所有文件会打破这个目的。

因此,我的问题可以重新表述为,rsync能否按字节比较文件?


@thatotherguy 但这意味着它甚至会更新那些根本没有区别的文件,对吗?这也不是一个选项,因为在替换它们之前,我会备份目标文件夹中的文件 - 这就是“-b”选项的用途。我将更新问题以强调这种行为。 - user4918296
1
你尝试过用逐字节比较与校验和计算相比时间吗?我认为你可能会想避免使用前者... - l'L'l
@l'L'l 谢谢! - hmedia1
@nautical,你这样使用cmp的方式——即使是为了生成一个文件列表以管道传输到rsync,也会将你节省的写入操作换成不必要的读取操作。相比之下,rsync在检测到更改时至少会停止比较,而使用cmp进行此操作的开销将被抵消甚至更多。 - hmedia1
@nautical 试一下吧。你又在得出结论了。如果你想的话,请使用cmpbashcp来制作自己的同步工具。你很快就会明白,出于我现在无法启示你的原因,你无法在所需的效率和准确性平衡方面超越rsync。请记住,是你提出了这个问题。如果我恰好知道答案,我很乐意提供澄清,但你似乎对需要rsync如何工作有坚定的信仰,而实际上并没有完全理解它。你接受的答案表明了这种缺乏理解。 - hmedia1
显示剩余4条评论
2个回答

6
问题: rsync是否能够基于字节进行文件比较?
严格来讲,是的:
- 它是以块为单位进行比较的,但您可以更改块大小。 - 您可以使用 --block-size=1(但这对于基本上所有情况都不合理和不适当)。
默认情况下,在网络上传输时使用基于块的滚动校验和。
使用 --no-whole-file 选项可以强制在本地使用此行为。 (见下文)
声明1. 在此情况下,计算校验和会导致速度降低。
这就是为什么默认情况下禁用本地传输的原因。
使用 --checksum 选项会强制读取整个文件,而不是默认的块对块增量传输校验和检查。
声明2. 现在,rsync 是否会逐字节检查本地源和目标文件,还是使用修改时间和/或大小来确定源文件是否需要备份?
默认情况下,它将使用大小和修改时间。
您可以使用 --size-only、--(no-)ignore-times、--ignore-existing 和 --checksum 的组合来修改此行为。
声明3. 我绝对不希望依靠文件大小或修改时间来决定是否应该进行备份。
然后您需要使用 --ignore-times 和/或 --checksum。
声明4. 如评论中所建议的提供 --ignore-times 不是一个选择。
也许使用 --no-whole-file 和 --ignore-times 正是您想要的? 这将强制使用增量传输算法,但对于每个文件都是如此,而不考虑时间戳或大小。
在我看来,只有在避免无意义的写入非常关键(尤其是要避免无意义的写入,而不是为了系统效率,因为对于本地文件来说进行增量传输实际上并不更有效),并且有理由相信具有相同修改时间戳和字节大小的文件确实可能不同时,才会使用这些选项的组合。
我认为按修改时间戳和字节大小比较文件是确定更改文件的逻辑第一步。
如果您比较以下两个文件:
  • 文件1(本地): File.bin - 79776451 字节,修改时间为5月15日07:51
  • 文件2(远程): File.bin - 79776451 字节,修改时间为5月15日07:51
  • 默认行为是跳过这些文件。 如果您不满意应该跳过这些文件,并希望它们进行比较,您可以使用 --no-whole-file --ignore-times 强制执行块与块之间的比较和差分更新。
    因此,在这一点上的总结是:
    1. 对于最有效的备份和归档,请使用默认方法
    2. 如果有必要,使用--ignore-times--no-whole-file 强制执行增量更改(块与块校验和,仅传输差异数据)
    3. 使用--checksum--ignore-times成为完全偏执而浪费的人。

    声明5。 请注意rsync指令中的-b选项。这意味着在替换之前将备份目标文件。

    是的,但这可以按您想要的方式工作,它并不一定意味着每次更新文件时都进行完整备份,并且肯定不意味着会完全传输。
    您可以配置rsync:
    • 保留一个或多个文件版本
    • 使用--backup-dir 配置它为完整的增量备份系统。
    以这种方式进行操作不会浪费空间,除了需要保留差异数据所需的空间外,实际上没有浪费空间。我可以验证这一点,因为我的备份驱动器上几乎没有足够的空间来存储所有以前版本的完整副本。

    一些补充信息


    为什么增量传输不比本地复制整个文件更有效?
    因为您没有跟踪每个文件的更改。如果您实际上有一个增量文件,您可以合并仅更改的字节,但是您首先需要知道这些更改的字节是什么。唯一能知道这一点的方法是通过读取整个文件。
    例如:
    我修改了一个10MB文件的第一个字节。 我使用带有增量传输的rsync同步此文件。 rsync立即看到第一个字节(或第一个块内的字节)已更改,并继续(默认情况下为--inplace)仅更改该块。 然而,rsync不知道只更改了第一个字节。它将保持校验和直到整个文件被读取。
    就所有目的而言:
    将rsync视为基于文件时间戳或大小是否更改有条件执行校验和的工具。覆盖此选项为--checksum基本等效于--no-whole-file和--ignore-times,因为两者都将:
    无论时间和大小如何,操作每个文件。 读取文件的每个块以确定要同步哪些块。
    那么好处在哪里呢?
    整个过程是传输带宽和速度/开销之间的权衡。
    --checksum是仅通过网络发送差异的好方法。 --checksum同时忽略具有相同时间戳和大小的文件是既仅通过网络发送差异的好方法,也最大化整个备份操作的速度。
    有趣的是,将--checksum用作全局选项可能比强制每个文件的增量传输要有效得多。

    1
    这是一个格式良好且详尽的回答,但我认为它没有正确回答主要问题 - rsync能否进行逐字节比较而不是校验和(出于性能原因)。 - Erki Aring
    1
    关于“不是这样工作”的部分:我提供了--ignore-times选项,它确实做到了这一点。在测试运行中,它为一个完全没有更改的文件生成了一个副本。我实际备份的文件夹有数千个文件。在实际情况下,几乎每个文件都会有一个副本,因此备份所需存储空间的一半将被浪费。 - user4918296
    感谢Erki和@nautical的反馈。这实际上是一个关于rsync程序使用的问题,我只是试图根据当时我所理解的问题为了那些可能会将rsync或部分答案实现到他们正在处理的解决方案中的人们的利益而添加到知识库中。现在问题已经被细化,我将编辑我的答案,以尝试更全面地回答直接的问题。 - hmedia1
    @nautical 我相信我的修改后的答案会更有帮助。我假设你的使用情况在这里是专业化的,因此我在尝试解决每个问题时没有做出任何假设。 - hmedia1
    1
    总的来说,这是一个不错的答案,但我不能点赞,因为(在我看来):1)说逐字节比较是可能的,但实际上是块与块之间的比较,使用校验和,这有点牵强(并且在问题的背景下,这是一个错误的答案,因为意图是避免校验和和开销);2)测试表明,使用“-b”和“--ignore-times”而没有使用“--checksum”会复制所有文件(另外,“--checksum”在这种情况下不等同于“--no-whole-file”和“--ignore-times”)。 - Erki Aring
    显示剩余13条评论

    1

    无法进行文件的逐字节比较,而不是使用校验和,这是您期望的方式。

    rsync的工作方式是创建两个进程:发送方和接收方,它们创建一个文件列表和它们的元数据来决定哪些文件需要更新。即使在本地文件的情况下也是如此,但在这种情况下,进程可以通过管道进行通信,而不是通过网络套接字。确定更改文件的列表后,更改将作为增量或整个文件发送。

    理论上,可以将整个文件发送到文件列表中以进行差异比较,但实际上,在许多情况下,这将非常低效。接收器需要将这些文件保存在内存中,以防它检测到需要更新文件,否则文件中的更改需要重新发送。这里可能的任何解决方案都听起来不太有效。

    有关rsync(理论)机制的良好概述:https://rsync.samba.org/how-rsync-works.html


    @ErkiA - 你回答的第一行事实上是错误的。进行逐字节比较是可能的。我已经清楚地解释了这一点。通过指定“--block-size=1”,您正在将块大小设置为1字节。这将非常低效并且需要很长时间。例如,对于每个字节,调试看起来像这样:chunk[3871960] offset=3871960 len=1 sum1=00740074 - hmedia1
    说实话,这个答案大部分是错误的。选择要同步的文件的谈判标准是一回事,另一回事是完成这个过程的方法。 - hmedia1
    @hmedia1 正如您自己的调试输出所显示的那样,这个“字节”实际上是一个块。这与 OP 所询问的逐字节比较几乎毫无关系。此外,您已经在谈论文件同步,但问题更多地涉及 rsync 应包括哪些文件在备份中(或更具体地说,应该跳过哪些文件)。 - Erki Aring
    一个单个字节块,是的。 因此效率低下。 根据给定的不可协商标准跳过哪些文件,需要了解更多概念。 OP希望确保具有相同名称,相同文件夹中,相同修改时间和相同大小的文件不应该成为跳过的标准。 但同时,OP也不希望浪费性地重新复制文件。 看到冲突了吗? "逐字节"基础的理解与提问的意图不符。 可以实现。 高效。 - hmedia1
    @nautical - 你现在搞清楚了。不幸的是,有一个“字面”答案和一个“实际意义上”的答案——问题在于,在澄清这些意图之前,两者都没有意义,因为这样做会产生一些含义。我现在会提供两个评论,一个针对每个答案。 - hmedia1
    显示剩余6条评论

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接