为什么移动文件夹中的某些文件比移动整个文件夹花费的时间更长?

为什么移动文件夹中的某些文件比移动整个文件夹花费的时间更长?

我的 ubuntu 云服务器上有数百万张图片。当我使用命令移动包含 1200 万张图片的完整文件夹时mv,几乎是瞬间完成的。但是,当我mv只移动图片(而不是文件夹)时,则需要一些时间。有没有办法像移动文件夹一样快速移动所有图片?

事情是这样的:

  1. src 文件夹有 1200 万张图片,我使用

    $ mv  src ../dst
    

    立即发生

  2. 在 src 文件夹中我执行以下操作来移动:

    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ {} +
    

    这需要一些时间。

有没有办法可以加快第二个过程?

答案1

总结: 不

对于较少的文件,你不需要find,但即使在这种简化和较小的情况下,如果你只是

mv *.jpg ../../dst/

这比一次移动整个目录需要更多时间。


为什么?关键是要了解什么mv是。

简而言之,mv将一个数字(用于标识目录或文件)从一个 inode(包含它的目录)移动到另一个 inode,并且这些索引会在文件系统的日志或 FAT 中更新(如果文件系统以这种方式实现)。

如果源和目标位于同一个文件系统上,则数据不会实际移动,而只是改变位置,即它们所附着的点。

因此,当你mv 目录,你正在执行此操作一度

但当你移动百万文件,你正在执行此操作100万次

举一个实际的例子,你有一棵有很多枝干的树。具体来说,有一个节点上有 100 万个枝干。
要剪掉这些枝干并将它们移到其他地方,你可以剪掉每一个枝干,这样你就剪掉了 100 万个枝干,或者你只剪掉节点前面的枝干,这样就只剪掉一个枝干(这就是移动文件和移动目录的区别)。

答案2

它仍然会很慢,因为如上所述,文件系统必须将每个文件名重新链接到其新位置。

但是,您可以加快现有速度。

find 命令会为每个文件运行一次 exec。因此,它会mv为 1200 万个文件启动 1200 万次该命令。这可以通过两种方式进行改进。

  • 在末尾添加一个加号:
    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ +
    检查手册页以确保它在您的版本中受支持find。效果应该是运行一系列mv命令,每个命令行上包含尽可能多的文件名。

  • 一起使用findxargs
    find -maxdepth 1 -name '*.jpg' -print0 | xargs -0 mv -t ../../dst/
    -print0使用 NUL(即零字节)来分隔文件名。 此加号xargs -0可修复xargs文件名中空格可能带来的任何问题。 该xargs命令将从命令中读取文件名列表find,并对尽可能多的文件名运行该mv命令。

答案3

您的困惑来自文件系统抽象,它让您相信文件夹以树状方式包含文件和其他文件夹。事实并非如此:文件系统中的所有文件和目录都位于同一级别,并根据实现以某种数字标识。目录只是包含其他文件列表的特殊文件。

当您在文件系统内“移动”文件时,实际文件不会移到任何地方。相反,目录内的列表会更新以反映更改。

mv src ../dst将单个列表条目从目录移动.到另一个目录../dst,因此速度很快。

find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/必须移动数百万个条目,因此速度较慢。如果mv只调用一次而不是每个文件调用一次,可能会加快速度,并且mv命令本身可以优化为一步移动多个目录条目,但没有办法使其像移动单个目录时一样快。

答案4

简化答案

移动文件只需 3 个步骤:

  • 将文件的链接添加到目标文件夹的 inode 列表中
  • 检查链接是否添加成功
  • 如果上述检查成功,则从源文件夹的 inode 列表中删除链接。

对于文件或文件夹来说,此过程是相同的。
显然,对 1 个文件执行此操作比对 100 个文件执行此操作快 100%。

man link 是 add()
man unlink是 remove()
mv只是使用上面的两个命令并在中间添加一个检查以防止数据丢失。

相关内容