多文件原子写入

6
假设我有一组文件。 如何确保对所有这些文件的写入是原子的。
我考虑过先写入临时文件,只有在写入成功后才对每个文件执行原子重命名。 但是,同时重命名所有文件并不是原子操作。 如果我们想要追加到这些文件中,则此方法也无法扩展到非常大的文件。
相反,我考虑实现事务,但这将成为一个单独的项目。 我意识到这基本上是关于实现小型数据库。
您会如何在Python中实现它?
d = FileWriter.open(['file1', 'file2'], 'wb+')
d.write('add hello world to files')
d.close()

确保d.write是原子性的,或者如果不成功,则回滚到原始文件。

我刚刚编写了一个FileWriter。天真地想,它会按顺序写入两个文件。如果中途出现问题,你就没办法了。想象一下,这些文件代表一个搜索引擎索引。因为你只写入了一个文件而没有写入另一个文件,所以现在你有了损坏的索引。 - Alex Ksikes
好的,我漏掉了一点。你需要对所有文件进行原子操作。 - mo.
4
为什么不直接使用数据库呢?这是它们所擅长的。 - jterrace
我认为我可以设置一个修复机制。如果出现问题,修复文件。 - Alex Ksikes
@AlexK:这个需要跨平台吗?如果不需要,是哪个操作系统? - Mechanical snail
显示剩余2条评论
4个回答

6

我的想法是,首先确保打开同步,然后执行以下操作:

  1. 写入临时文件:file1~、file2~和特殊文件success~(必须先写入成功)。
  2. 成功写入后,删除文件success~。
  3. 将文件重命名为file1和file2。

如果发生故障:

  1. 检查是否存在success~。
  2. 如果存在,则无需修复。因为文件未更新(没有重命名),所以隐含地执行了回滚。
  3. 如果不存在success~,则在写入和重命名期间出现故障。在这种情况下,修复的方法就是将filex~重命名为filex。

1
为什么要费这么大劲,当数据库已经内置了这个功能? - Burhan Khalid
因为我希望这些文件是纯文本,易于人类阅读。我也希望这些文件能够轻松地在用户之间共享。事实上,我希望任何知道这些文件格式的人都能够创建它们,而无需使用x数据库。这是为了一个迷你搜索引擎索引。 - Alex Ksikes
2
再加上,这有点像回答关于熨烫西装裤的问题,建议只穿蓝色牛仔裤一样。这不友好,也肯定不会有帮助。 - yeoman

2

根据您目前的需求陈述,唯一合理的答案是需要操作系统级别的支持,这甚至不再是一个严格的Python相关问题。例如,请参阅有关事务性文件系统的内容herehere。以下是有关迄今为止提出的解决方案的简短摘录:

在没有文件系统事务的情况下,确保多个文件系统操作的一致性是困难的,甚至是不可能的。文件锁定可以用作单个文件的并发控制机制,但通常无法保护目录结构或文件元数据。例如,文件锁定无法防止符号链接上的TOCTTOU竞争条件。文件锁定也无法自动回滚失败的操作,例如软件升级;这需要原子性。

因此,我建议重新思考您的问题(也许还有您的问题?)并更详细地分析您的要求。底线是,您需要的保证越少,您就可以找到越简单的解决方案。也许您可以通过像文件锁定这样简单的东西来解决问题,或者您会发现需要使用数据库。

说到这个,如果你正在考虑一个文件系统,因为你的架构组件需要访问文件,那么你是否考虑过使用FUSE来构建一个类似文件系统的外观,放在一个常规数据库之上呢?使用python-fuse非常容易。

实际上在我的情况下,似乎一个修复机制就足够了。尝试写入这些文件,如果出现问题,则在以后的阶段修复它们。 - Alex Ksikes

1
数据库管理系统通常使用一个写前日志文件或者自定义文件系统的原因是,通常的文件系统非常糟糕,无法保证原子性或执行顺序。它们完全优化了一致性和性能。因此,只有对单个文件的写入顺序可以信任,除非您创建具有专用文件系统的分区或映像文件。您可以在每个写入多个文件时写入事务号码,并明确表明写入是否完成,例如使用具有开始/结束标记的xml或json样式块,如<element>...</element>或{...}等。然后,您的代码可以轻松检测跨多个文件的任何间隙,并在崩溃后确定最后一致状态。为了避免在缓存中等待几分钟的某些写入导致崩溃后的最后一致状态变得任意老,可以将任何这些方法与sync/fsync相结合。使用sync/fsync也使事务提交变得有点可能,即从文件系统的角度保证至少已经写入了到目前为止的所有内容。

您的存储系统可能因为电源故障而丢失最后一次写入,无论是具有内部缓存的硬盘或固态硬盘(SSD),网络附加存储(NAS)等。这些系统提供的保证可能相差很大,对于所有的方法都需要考虑这个挑战,无论是使用文件系统还是传统的关系型数据库(RDBMS)进行存储。

如果您正在写入内置硬盘或SSD,则使用不间断电源(UPS)肯定是一个不错的选择,特别是当UPS发出关机信号时,您可以以受控的方式关闭系统。


0
你可以尝试使用 flock 来实现你的目标。在 Python 中,可以通过 fcntl.flock 实现。请注意,这只是一种建议性措施。为了真正保证你想要的结果,你应该尝试使用支持严格锁定的数据库、文件系统或内核。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接