【发布时间】:2010-12-10 00:07:07
【问题描述】:
我正在尝试一次删除 10000 多个文件,例如以原子方式删除要么全部删除,要么全部保留。
当然,显而易见的答案是将所有文件移动到一个临时目录中,并在成功时递归删除它,但这会使所需的 I/O 量翻倍。
压缩不起作用,因为1)我不知道哪些文件需要删除,2)文件需要经常编辑。
有什么东西可以帮助降低 I/O 成本吗?任何平台都可以。
编辑:假设停电随时可能发生。
【问题讨论】:
我正在尝试一次删除 10000 多个文件,例如以原子方式删除要么全部删除,要么全部保留。
当然,显而易见的答案是将所有文件移动到一个临时目录中,并在成功时递归删除它,但这会使所需的 I/O 量翻倍。
压缩不起作用,因为1)我不知道哪些文件需要删除,2)文件需要经常编辑。
有什么东西可以帮助降低 I/O 成本吗?任何平台都可以。
编辑:假设停电随时可能发生。
【问题讨论】:
Kibbee 是正确的:您正在寻找交易。但是,如果您不想的话,您不需要依赖数据库或特殊文件系统功能。交易的本质是这样的:
请注意,在第 (1) 步之后的任何时候,您都可以重新启动应用程序,它会继续删除逻辑删除的文件,直到它们最终全部消失。
请注意,您不应该走得太远:否则您将开始重新实现真正的交易系统。但是,如果您只需要很少的简单事务,那么您自己的方法可能是可以接受的。
【讨论】:
您的问题的基本答案是“不”。更复杂的答案是,这需要文件系统的支持,而且很少有文件系统有这种支持。显然 NT 有一个事务性 FS 支持这一点。 BtrFS for Linux 可能也会支持这一点。
在没有直接支持的情况下,我认为硬链接、移动、删除选项是您将获得的最佳选择。
【讨论】:
在 Windows Vista 或更高版本上,Transactional 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);
【讨论】:
您不能只建立要删除的路径名列表,将此列表写入文件to_be_deleted.log,确保该文件已进入磁盘(fsync()),然后开始执行删除操作。完成所有删除后,删除to_be_deleted.log 事务日志。
当您启动应用程序时,它应该检查to_be_deleted.log 是否存在,如果存在,则重播该文件中的删除操作(忽略“不存在”错误)。
【讨论】:
您是否有用于访问文件的抽象层(例如数据库)? (如果您的软件直接进入文件系统,那么我的建议不适用)。
如果删除文件的条件是“正确”,则在抽象层中将状态更改为“已删除”并开始后台作业以“真正”从文件系统中删除它们。
当然,这个提议在打开/关闭文件时会产生一定的成本,但会在创建符号链接等方面为您节省一些 I/O。
【讨论】:
我认为复制然后删除方法几乎是执行此操作的标准方法。你知道你不能容忍额外的 I/O 吗?
我不会把自己算作文件系统的导出,但我想任何执行事务的实现都需要首先尝试执行所有所需的操作,然后它需要返回并提交那些行动。 IE。与非原子方式相比,您无法避免执行更多的 I/O。
【讨论】:
在 *nix 上,在单个文件系统中移动文件是一项成本非常低的操作,它通过硬链接到新名称然后取消链接原始文件来工作。它甚至不会改变任何文件时间。
如果您可以将文件移动到单个目录中,那么您可以重命名该目录以使其成为真正的原子操作,然后稍后以较慢的非原子操作删除文件(和目录)时尚。
你确定你不只是想要一个数据库吗?它们都内置了事务提交和回滚。
【讨论】:
unlink(2) 有更多的开销,但这就是进行交易的代价。不过,我可能应该这么说。
我认为您真正需要的是进行交易的能力。由于光盘一次只能写入一个扇区,因此您一次只能删除一个文件。如果其中一个删除没有成功发生,您需要的是能够回滚以前的删除。像这样的任务通常是为数据库保留的。您的文件系统是否可以执行事务取决于您使用的文件系统和操作系统。 Windows Vista 上的 NTFS 支持Transactional NTFS。我不太确定它是如何工作的,但它可能很有用。
此外,Windows 中还有一个名为 shadow copy 的东西,在 Linux 世界中称为 LVM Snapshot。基本上它是光盘在某个时间点的快照。您可以在删除之前直接拍摄快照,如果没有成功,请将文件从快照中复制回来。我已经在 VBScript 中使用 WMI 创建了卷影副本,我确信 C/C++ 也存在类似的 api。
关于卷影复制和 LVM 快照的一件事。整个分区的工作。因此,您不能只拍摄单个目录的快照。但是,拍摄整个磁盘的快照只需要几秒钟。所以你会拍一张快照。删除文件,如果不成功,将文件从快照中复制回来。这会很慢,但取决于您计划回滚的频率,它可能是可以接受的。另一个想法是恢复整个快照。这可能好也可能不好,因为它会回滚整个磁盘上的所有更改。如果您的操作系统或其他重要文件位于那里,那就不好了。如果此分区仅包含您要删除的文件,则恢复整个快照可能会更容易、更快捷。
【讨论】:
不要移动文件,而是在临时目录中建立符号链接。然后,如果一切正常,请删除文件。或者,只需在某处列出文件列表,然后将其删除。
【讨论】: