我试图一次删除10000多个文件,例如原子地删除要么都需要立即删除,要么都需要留在原地。

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

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

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

编辑:我们假设任何时候都可能发生停电。

有帮助吗?

解决方案

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

  1. 将记录写入特殊文件(通常称为“日志”),列出要删除的文件。
  2. 安全地写入此记录后,请确保您的应用程序的行为就像文件实际已被删除一样。
  3. 稍后,开始删除交易记录中指定的文件。
  4. 删除所有文件后,删除交易记录。
  5. 请注意,在步骤(1)之后的任何时间,您都可以重新启动应用程序,它将继续删除逻辑删除的文件,直到它们最终全部消失。

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

其他提示

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

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

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

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

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

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

不是移动文件,而是将符号链接放入临时目录。然后,如果一切正常,请删除文件。或者,只需在某处创建文件列表,然后删除它们。

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

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

我认为copy-and-then-delete方法几乎是执行此操作的标准方法。您是否知道您无法容忍额外的I / O?

我不认为自己是文件系统的导出,但我认为执行事务的任何实现都需要首先尝试执行所有需要的操作,然后它需要返回并提交那些动作。 I.E.你不能避免比非原子地执行更多的I / O.

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

如果条件是“正确”的话。删除文件,将状态更改为“已删除”在你的抽象层中,开始一个后台工作“真的”从文件系统中删除它们。

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

在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);

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top