![批量删除 ZFS 上的大目录,无需递归遍历它](https://linux22.com/image/70164/%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4%20ZFS%20%E4%B8%8A%E7%9A%84%E5%A4%A7%E7%9B%AE%E5%BD%95%EF%BC%8C%E6%97%A0%E9%9C%80%E9%80%92%E5%BD%92%E9%81%8D%E5%8E%86%E5%AE%83.png)
我想删除zfs
数据集子目录的内容。这是一个很大的数据量。对于池“nas”,路径是/nas/dataset/certainFolder
$ du -h -d 1 certainFolder/
1.2T certainFolder/
而不是我必须等待,rm -rf certainFolder/
我不能只销毁该目录的句柄,以便它可以覆盖(如果我选择重新创建它,即使是相同的目录名称)?
因此,对于例如不太了解zfs
文件系统内部结构,特别是它如何记录其文件,我想知道我是否能够直接访问该日志/映射,例如,然后删除正确的条目,以便目录不再显示。该空间目录也必须从某种审计中删除。
是否有捷径可寻?即使在外部3fs,或者这已经是递归删除命令首先要做的事情,即窃取并编辑日志?
我只是希望做一些类似的事情,kill thisDir
它只是删除某种 ID,并且目录不再显示在ls -la
.显然,数据仍然在驱动器上,但空间现在将被重用(覆盖),因为 ZFS 就是那么酷?
我的意思是我认为兹夫斯真的那么酷,我们该怎么做呢?理想情况下?双手搓在一起 :-)
我的具体用例(除了我对 的热爱zfs
)是备份存档的管理。数据通过 SMB 上的 win 盒子上的 freefilesync (AWESOME PROG) 推送到 zfs 到 zfs 池。当删除rm -rf /nas/dataset/certainFolder
一个putty
术语时,它会停止,该术语显然已经很长时间无法使用了。我当然必须打开另一个终端才能继续。那已经过时了,而且监控 rm -rf 也没什么乐趣,可能需要几个小时。
也许我应该设置命令来释放句柄,例如&
,然后打印到标准输出,这可能会很好。 更现实zfs destroy nas/dataset; zfs create -p -o compression=on nas/dataset
,在考虑@Gilles 的响应后几秒钟内重新创建数据集。
答案1
在任何像样的文件系统中,跟踪释放的块是不可避免的,而 ZFS 是没有例外。然而,在 ZFS 下有一种简单的方法可以几乎瞬时删除目录通过“推迟”底层清理。它在技术上与吉尔斯的建议非常相似,但本质上是可靠的,不需要额外的代码。
如果您在删除目录之前创建文件系统的快照,则目录删除将非常快,因为不需要在其下探索/释放任何内容,所有内容仍由快照引用。然后您可以在后台销毁快照,以便空间逐渐恢复。
d=yourPoolName/BackupRootDir/hostNameYourPc/somesubdir
zfs snapshot ${d}@quickdelete && {
rm -rf /${d}/certainFolder
zfs destroy ${d}@quickdelete &
}
答案2
你所要求的是不可能的。或者,更准确地说,删除目录及其文件时需要付出代价;如果您在删除时未付款,则必须在其他地方付款。
您不仅仅是删除一个目录——这几乎是瞬时的。您将删除一个目录及其中的所有文件,并且同样递归地删除其所有子目录。删除文件意味着减少其链接计数,然后如果链接计数达到 0 并且文件未达到 0,则将其资源(用于文件内容和文件元数据的块,以及索引节点(如果文件系统使用 inode 表))标记为空闲打开。这是对目录树中的每个文件都必须执行的操作,因此所花费的时间至少与文件的数量成正比。
您可以延迟将资源标记为免费的成本。例如,有垃圾收集文件系统,您可以在其中删除目录而不删除它包含的文件。垃圾收集器的运行将检测无法通过目录结构访问的文件并将它们标记为空闲。在垃圾收集文件系统上执行的操作与在传统文件系统上执行rm -f directory; garbage-collect
的操作相同,但触发器不同。rm -rf
很少有垃圾收集的文件系统,因为 GC 会带来额外的复杂性,而很少需要。当文件系统需要一些空闲块但找不到任何空闲块时,GC 时间可能随时出现,因此操作的性能将取决于过去的历史记录,而不仅仅是操作,这通常是不可取的。您需要运行垃圾收集器才能获得实际的可用空间量。
如果你想在普通文件系统上模拟 GC 行为,你可以这样做:
mv directory .DELETING; rm -rf .DELETING &
(我省略了许多重要的细节,例如错误检查、断电恢复能力等)目录名称立即变得不存在;空间被逐步回收。
避免在没有 GC 的情况下在移除期间支付成本的另一种方法是在分配期间支付。将目录树标记为已删除,并在分配块时遍历已删除的目录。这很难与硬链接协调,但在没有硬链接的文件系统上,可以通过分配 O(1) 成本增加来完成。然而,这将使非常常见的操作(创建或放大文件)更加昂贵,唯一的好处是相对罕见的操作(删除大目录树)更便宜。
如果目录树存储为其自己的块池,则可以批量删除该目录树。 (注意:我使用的“池”一词与 ZFS 的“存储池”的含义不同。我不知道正确的术语是什么。)这可能会非常快。但是您如何处理可用空间呢?如果将其重新分配给另一个池,则会产生成本,尽管比单独删除文件要少得多。如果将该空间保留为未使用的保留空间,则无法立即回收它。为目录树拥有单独的池意味着增加或减少该池大小(动态或显式)的成本增加。使树成为自己的存储池也会增加将文件移入和移出树的成本。
答案3
如果必须快速,我会生成一个新的临时目录,即mv
它下面的目录,然后递归删除临时目录:
t=`mktemp -d`
mv certainFolder $t/
rm -rf $t &
答案4
如果您要删除并快速重新创建的文件夹位于其自己的数据集中(而不仅仅是另一个数据集的子目录),您可以执行以下操作:
zfs rename pool/dataset pool/dataset.old
zfs create -o ...options... pool/dataset
zfs destroy -r pool/dataset.old
pool/dataset
当旧的被销毁时,新的可以立即使用。
如果您不想删除任何子数据集(例如pool/dataset/child
,它将与其父数据集一起重命名为pool/dataset.old/child
),那么情况会稍微复杂一些,但也不会比您想要删除子目录更复杂。删除大部分子目录时保留。只需将它们重命名回新名称即可pool/dataset
前破坏pool/dataset.old
,例如 zfs rename pool/dataset.old/child pool/dataset/child
。同样,您将 backmv
的子目录pool/dataset.old
放入pool/dataset
.
如果它只是一个子目录,您可以像在任何其他文件系统上一样执行以下操作:
mv subdir subdir.old
mkdir subdir
chmod ... subdir ; chown ... subdir # if and as required
rm -rf subdir.old/ &
这与吉尔斯在回答中所说的几乎相同。
同样,如果您想保留子子目录,请在运行之前将它们移回到新的子目录中rm -rf
,例如mv subdir/child subdir/
。
我这样做已经有几十年了,至少从 20 世纪 90 年代开始——这个zfs rename
版本只是同一方法的明显演变。我不记得目录是否可以在 MS-DOS 中重命名,但如果可以的话,我可能也在 20 世纪 80 年代使用 MS-DOS 这样做过。
顺便说一句,对于数据集和子目录,您不必.old
立即删除它们。我倾向于保留它们,直到我确定我已经检索到我想要从它们那里保留的所有内容,或者直到我需要恢复它们正在使用的磁盘空间。我喜欢尽可能拖延不归路。
顺便说一句,使用数据集而不是子目录 ZFS 通常是一个好主意,因为您可以在每个数据集上有不同的设置(例如压缩类型、配额、保留、atime/relatime、加密等),并且每个数据集可以是单独拍摄快照和备份zfs send
。
但这样做的代价是,将文件或子目录树移动到另一个数据集是一个复制和删除操作,与将它们移动到不同磁盘、分区或 LV 等上的另一个文件系统时的操作相同。数据集是实际上是一个不同的、独立的文件系统,有自己的挂载点。看如何将文件从一个 zfs 文件系统移动到同一池中的不同 zfs 文件系统?- 那里的一条评论让我今天来到这里。
另外值得注意的是:数据集的挂载点可以是任何地方在文件系统层次结构中,它不必直接安装在其父级下,并且可以根据需要更改安装点。例如,如果我有一个名为“rpool”的小型 SSD 根池和一个名为“export”的用于批量数据的大型 HDD 池,我可以移动 rpool 的大型子目录(和数据集)进行导出,并且仍然将它们安装在同一位置。例如
zfs create export/share-doc
mv /usr/share/doc/* /export/share-doc/
zfs set mountpoint=/usr/share/doc export/share-doc
(这是一个简化的示例。在实践中,我倾向于复制 fs 层次结构 - 例如,为 export/usr、export/usr/share、export/usr/share/doc 创建数据集到 a)保持池的顶层整洁b)如果我需要将其他子目录或数据集移动到那里)
理解数据集名称和层次结构与挂载点之间的区别很重要。如果您递归地销毁数据集,则其子数据集也将被销毁,无论它们安装在何处。因此,请记住为您想要保留的所有孩子重新命名。