【问题标题】:Why is it important to delete files in-order to remove them faster?为什么删除文件以更快地删除它们很重要?
【发布时间】:2013-07-31 02:20:49
【问题描述】:

前段时间我了解到rsync deletes files much faster that many other tools

几天前我遇到了this wonderful answer on Serverfault,这解释了为什么rsync 如此擅长删除文件。

引用该答案:

我今天重新访问了这个,因为大多数文件系统都存储它们的目录 btree 格式的结构,删除文件的顺序是 也很重要。需要避免重新平衡 btree 时 执行取消链接。因此,我在删除发生之前添加了一个排序。

您能否解释按顺序删除文件如何防止或减少 btree 重新平衡的次数?


我希望答案能显示删除顺序如何提高删除速度,并详细说明在btree 级别发生的情况。编写rsync 和其他程序(请参阅问题中的链接)的人使用这些知识来创建更好的程序。我认为对于其他程序员来说,拥有这种理解才能编写出更好的软件是很重要的。

【问题讨论】:

  • 您的 rsync 链接不再有效。 This archive link 工作;我在this related question找到它。
  • 我现在已经对rm -rrsync 进行了基准测试。它们都显示了在 ext4 和 xfs 上删除包含 n 个文件的目录的 O(n²) 性能。 coreutils patch from 2008 声称这是固定的并且应该具有 O(n) 性能。我提交了一个关于 here 的新错误

标签: algorithm filesystems b-tree delete-file


【解决方案1】:

这不重要,也不是b-tree问题。这只是巧合

首先,这非常依赖于实现并且非常依赖于 ext3。这就是为什么我说它不重要(一般用途)。否则,请放置 ext3 标记或编辑摘要行。

其次,ext3使用 b-tree 作为目录条目索引。它使用 Htree。 Htree 与 b-tree 相似,但不同,它是 not require balancing。在fs/ext3/dir.c中搜索“htree”。

由于基于 htree 的索引,a) ext3 与 ext2 相比具有更快的查找速度,但 b)readdir() 按哈希值顺序返回条目。哈希值顺序相对于文件创建时间或数据的物理布局是随机的。众所周知,随机访问比在旋转媒体上的顺序访问要慢得多。

A paper on ext3 published for OLS 2005 by Mingming Cao, et al. 建议(强调我的):

按 inode 编号对 readdir() 返回的目录条目进行排序。

现在,进入 rsync。 Rsync 按文件名对文件进行排序。请参阅flist.c::fsort()flist.c::file_compare()flist.c::f_name_cmp()

我没有测试以下假设,因为我没有来自 @MIfe got 43 seconds 的数据集。但我假设与 readdir() 返回的随机顺序相比,按名称排序更接近最佳顺序。这就是为什么您在 ext3 上使用 rsync 可以看到更快的结果。如果你生成 1000000 个随机文件名的文件,然后用 rsync 删除它们怎么办?你看到同样的结果吗?

【讨论】:

  • 谢谢!我无法掌握一件事。排序后我们还是一个一个地删除文件,所以这仍然是随机访问(只是猜测)。如果将这些文件单独发送给文件系统进行删除,文件系统如何获得按顺序删除文件的优势?
  • 我会说它是顺序的,因为 Linux 块层在到达旋转媒体之前会合并来自文件系统层的近距离请求。如您所知,unlink(2) 不会擦除整个文件,而是在文件系统中将它们标记为“已删除”。这些标记,包括块指针,彼此非常接近。关键是因为我们谈论的是非常非常大的目录,所以有大量的标记占用了很大的空间。与顺序合并访问相比,随机访问空间不会给块层任何合并请求的机会并导致重大开销。
  • 如果我理解正确,操作系统应该首先从目录中删除文件,然后将 inode 标记为空。这应该会导致在这些文件夹和单个文件的 inode 结构之间移动 HDD 磁头。 Linux 是否能够以利用顺序 inode 排序的方式批量删除多个文件(似乎它应该首先完成文件夹,然后为所有这些文件批量更新 inode)。
  • 我不知道。我想 Unix/Posix/Linux 系统调用被设计得很简单。删除目录并不总是删除其中的文件;有一种东西叫做“硬链接”。所以,即使你有这样的系统调用,它也必须一一检查 inode。
  • 你是对的,这是巧合,在测试时我按词法顺序(file1,file2,file3)创建了文件,所以inode自然会按照相同的顺序。
【解决方案2】:

假设您发布的答案是正确的,并且给定的文件系统确实将内容存储在平衡树中。平衡树是一项非常昂贵的操作。保持树“部分”平衡非常简单,因为当您允许树稍微不平衡时,您只需担心在插入/删除点周围移动事物。然而,当谈到完全平衡的树时,当你移除一个给定的节点时,你可能会突然发现,这个节点的子节点可能属于树的完全相反的一侧,或者对侧的一个子节点已经成为根节点,并且它的所有子节点都需要在树上向上旋转。这需要您进行一连串的旋转,或者将所有项目放入一个数组中并重新创建树。

            5
    3               7
2       4       6       8

现在去掉 7,很简单吧?

            5
    3               8
2       4       6       

现在去掉 6,还是很容易的,是吗...?

            5
    3               8
2       4       

现在去掉8,呃哦

            5
    3               
2       4

让这棵树成为适当的平衡形式,例如:

        4
    3       5
2

这是相当昂贵的,至少与我们所做的其他移除相比,并且随着我们树的深度增加而呈指数级恶化。在移除 8 之前,我们可以通过移除 2 和 4 来加快速度(指数级地)。特别是如果我们的树的深度超过 3 层。

没有排序删除平均为 O(K * log_I(N)^2)。 N 表示元素总数,K 表示要删除的数量,I 是允许给定节点的子节点数,log_I(N) 然后是深度,对于每个深度级别,我们以二次方的方式增加操作次数。

通过一些排序帮助移除平均需要 O(K * log_I(N)),但有时排序无法帮助您,并且您无法移除需要重新平衡的东西。不过,将其最小化是最佳选择。

编辑:

另一种可能的树排序方案:

            8
    6               7   
1       2       3       4

在这种情况下实现最佳移除会更容易,因为我们可以利用我们对事物排序方式的了解。在任何一种情况下都有可能,实际上两者都是相同的,在这种情况下,逻辑更容易理解,因为对于给定的场景,排序更人性化。在任何一种情况下,按顺序都被定义为“首先删除最远的叶子”,在这种情况下,最远的叶子恰好也是最小的数字,我们可以利用这一事实使它更小更优化,但对于所展示的文件系统示例,这一事实不一定正确(尽管可能如此)。

【讨论】:

  • 据我所知,按顺序删除会导致不平衡(例如,如果我们删除 2 3 4,则需要重新平衡树)。我看不出按顺序删除有什么帮助。
  • 我选择的排序是为了演示平衡二叉树是如何工作的,而不是为了演示文件系统将使用的删除或排序的最佳排序。逻辑与给定节点允许拥有的子节点数量相同,二叉树(2 个子节点)最容易在文本中演示。
  • 但在那篇文章中,作者似乎使用普通排序来减少重新平衡的次数。
  • 我不在乎作者使用什么排序,这篇文章的重点是展示痛苦再平衡的原因,以及为什么避免它是好的,以及 rsync 如何通过以下方式实现更快的性能这样做!又名:回答问题:您能否解释按顺序删除文件如何防止或减少 btree 重新平衡的数量? “有序”并不总是意味着显而易见的事情。在这种情况下,顺序意味着最远的叶子在树上,而不是 1
  • 相反,我们可以更改树本身的排序标准,使两者的含义相同。 :)
【解决方案3】:

如果您按顺序删除文件,我不相信 B-tree 重新平衡的数量会发生显着变化。但是,我确实相信,如果您这样做,对外部存储的不同搜索次数将会大大减少。在任何时候,B 树中唯一需要访问的节点将是树的最右边界,而在随机顺序下,每个文件以相同的概率访问 B 树中的每个叶块。

【讨论】:

  • 是的。你可能是对的。 Btree 用于硬盘文件系统是有原因的,它是更快的顺序访问。所以如果我们按顺序遍历这些叶子,结果应该会快很多。另一方面,当我们从这些叶子中删除节点时,我们最终会重新平衡,以便访问另一个 btree 叶子,从而进行随机访问,这很慢。另一方面,无论如何都会发生再平衡,但至少我们按顺序遍历叶子,从而减少开销。
【解决方案4】:

B-Tree 的重新平衡比 B-Tree+ 实现更便宜,这就是大多数文件系统和数据库索引实现都使用它们的原因。

删除时有很多方法,根据方法的不同,它在时间和重新平衡树的需要方面可能更有效。您还必须考虑节点的大小,因为节点可以存储的键数量会影响重新平衡树的需要。较大的节点大小只会对节点内的键重新排序,但较小的节点可能会使树重新平衡很多次。

了解这一点的一个很好的资源是著名的 CLR (Thomas Cormen) 书籍“算法简介”。

【讨论】:

  • 谢谢!我开始这个问题是因为我对这个话题很感兴趣。我从朋友那里得到了关于rsync快速删除的转发,他们继续猜测它为什么这么快(他们猜测它使用了多个线程等)。所以这个话题对于很多程序员来说真的很有趣。在 StackOverflow 上,我收到了很多深入细节的很好的答案,提供了源代码的链接(例如 stackoverflow.com/a/17845560/862380),并且比许多书籍更好地解释了事情(stackoverflow.com/a/9513423/862380)...
  • 所以我开始了这个问题,因为它可能对很多人来说很有趣。一个答案显示了rsync 如何删除带有文件系统级别发生的细节的东西的示例,这将使所有这些人都能理解,甚至可能使他们在某种意义上成为更好的专家。对我来说,从现实生活中的例子中学习比使用经典的理论书籍来追求它们更有趣和更有动力。
【解决方案5】:

在托管大量目录的存储系统上,缓冲区缓存将承受压力,并且缓冲区可能会被回收。因此,如果您有按时间间隔的删除,那么在删除之间将 btree 重新返回到缓冲区缓存中的磁盘读取次数可能会很高。

如果您对要删除的文件进行排序,则实际上是在延迟删除并将它们捆绑在一起。这可能会产生每个 btree 分页块的更多删除的副作用。如果有统计数据可以说明两次实验之间的缓冲区缓存命中是多少,它可能会判断这个 hypo 是否错误。

但是,如果在删除过程中缓冲区缓存没有压力,那么 btree 块可能会留在核心中,那么我的假设就不成立了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 2020-09-25
    • 2020-01-09
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    相关资源
    最近更新 更多