5

我试图一次删除 10000 多个文件,例如,要么全部删除,要么全部保留。

当然,显而易见的答案是将所有文件移动到一个临时目录中,并在成功时递归地删除它,但这会使所需的 I/O 量翻倍。

压缩不起作用,因为1)我不知道哪些文件需要删除,2)文件需要经常编辑。

有什么东西可以帮助降低 I/O 成本吗?任何平台都可以。

编辑:让我们假设停电随时可能发生。

4

9 回答 9

13

Kibbee 是正确的:您正在寻找交易。但是,如果您不想的话,您不需要依赖数据库或特殊文件系统功能。交易的本质是这样的:

  1. 将记录写入一个特殊文件(通常称为“日志”),其中列出了您要删除的文件。
  2. 安全写入此记录后,请确保您的应用程序的行为就像文件已被实际删除一样。
  3. 稍后,开始删除交易记录中命名的文件。
  4. 删除所有文件后,删除交易记录。

请注意,在步骤 (1) 之后的任何时候,您都可以重新启动应用程序,它将继续删除逻辑删除的文件,直到它们最终全部消失。

请注意,您不应该走得太远:否则您将开始重新实现真正的交易系统。但是,如果您只需要很少的简单事务,那么您自己的方法可能是可以接受的。

于 2009-10-26T00:40:51.880 回答
6

在 *nix 上,在单个文件系统中移动文件是一项成本非常低的操作,它通过硬链接到新名称然后取消链接原始文件来工作。它甚至不会更改任何文件时间。

如果您可以将文件移动到单个目录中,那么您可以重命名该目录以使其成为真正的原子操作,然后稍后以较慢的非原子方式删除文件(和目录)。

你确定你不只是想要一个数据库吗?它们都内置了事务提交和回滚。

于 2009-10-26T00:33:42.037 回答
5

我认为您真正需要的是进行交易的能力。由于光盘一次只能写入一个扇区,因此您一次只能删除一个文件。如果其中一个删除没有成功发生,您需要的是能够回滚以前的删除。像这样的任务通常是为数据库保留的。您的文件系统是否可以执行事务取决于您使用的文件系统和操作系统。Windows Vista 上的 NTFS 支持事务性 NTFS。我不太确定它是如何工作的,但它可能很有用。

此外,还有一种称为Windows卷影副本的东西,在 Linux 世界中称为LVM 快照。基本上它是光盘在某个时间点的快照。您可以在删除之前直接拍摄快照,如果没有成功,请将文件从快照中复制回来。我已经在 VBScript 中使用 WMI 创建了卷影副本,我确信 C/C++ 也存在类似的 api。

关于卷影复制和 LVM 快照的一件事。整个分区的工作。因此,您不能只拍摄单个目录的快照。但是,拍摄整个磁盘的快照只需要几秒钟。所以你会拍一张快照。删除文件,如果不成功,将文件从快照中复制回来。这会很慢,但取决于您计划回滚的频率,这可能是可以接受的。另一个想法是恢复整个快照。这可能好也可能不好,因为它会回滚整个磁盘上的所有更改。如果您的操作系统或其他重要文件位于那里,那就不好了。如果此分区仅包含您要删除的文件,则恢复整个快照可能会更容易、更快捷。

于 2009-10-26T00:32:36.577 回答
2

不要移动文件,而是在临时目录中建立符号链接。然后,如果一切正常,请删除文件。或者,只需在某处列出文件列表,然后将其删除。

于 2009-10-26T00:31:48.637 回答
2

难道你不能只建立要删除的路径名列表,将此列表写入文件to_be_deleted.log,确保该文件已命中磁盘(fsync()),然后开始执行删除操作。完成所有删除后,删除to_be_deleted.log事务日志。

当您启动应用程序时,它应该检查 是否存在to_be_deleted.log,如果存在,则重播该文件中的删除(忽略“不存在”错误)。

于 2009-10-26T00:39:03.730 回答
2

您的问题的基本答案是“不”。更复杂的答案是,这需要文件系统的支持,而且很少有文件系统有这种支持。显然 NT 有一个事务性 FS 支持这一点。BtrFS for Linux 也可能支持这一点。

在没有直接支持的情况下,我认为硬链接、移动、删除选项是您将获得的最佳选择。

于 2009-10-26T01:03:14.663 回答
1

我认为复制然后删除方法几乎是执行此操作的标准方法。你知道你不能容忍额外的 I/O 吗?

我不会把自己算作文件系统的导出,但我想任何执行事务的实现都需要首先尝试执行所有所需的操作,然后它需要返回并提交这些操作。IE 你无法避免执行比非原子执行更多的 I/O。

于 2009-10-26T00:36:07.303 回答
1

您是否有用于访问文件的抽象层(例如数据库)?(如果您的软件直接进入文件系统,那么我的建议不适用)。

如果删除文件的条件“正确”,请在抽象层中将状态更改为“已删除”,并开始后台作业以“真正”从文件系统中删除它们。

当然,这个建议在打开/关闭文件时会产生一定的成本,但会在创建符号链接等方面为您节省一些 I/O。

于 2009-10-26T00:37:32.980 回答
1

在 Windows Vista 或更高版本上,事务性 NTFS应该满足您的需求:

HANDLE txn = CreateTransaction(NULL, 0, 0, 0, 0, NULL /* or timeout */, TEXT("Deleting stuff"));
if (txn == INVALID_HANDLE_VALUE) {
  /* explode */
}
if (!DeleteFileTransacted(filename, txn)) {
  RollbackTransaction(txn); // You saw nothing.
  CloseHandle(txn);
  die_horribly();
}
if (!CommitTransaction(txn)) {
  CloseHandle(txn);
  die_horribly();
}
CloseHandle(txn);
于 2009-10-26T00:50:57.787 回答