我想知道是否有类似于数据库的ACID属性的文件写入函数。原因是,我希望确保如果断电,我的文件写入操作不会出错并损坏文件。
根据您对文件的具体操作和平台,有几个选项:
如果您需要重复将内存中的 blob 序列化到磁盘上以保持状态(例如:dhcp 租约文件),并且您正在使用 Posix 系统,那么您可以将数据写入临时文件,然后将临时文件“重命名”为目标文件。在符合 Posix 标准的系统上,这保证是原子操作,即使文件系统是否具有日志功能也不会有影响。如果您使用的是 Windows 系统,则可以通过绑定利用名为 MoveFileTransacted 的本地函数。但是,关键概念在于,临时文件可保护您的数据,如果系统重新启动,则最坏情况是您的文件包含最后一次成功刷新数据的内容。此选项要求每次想记录更改时都必须完整地写出整个文件。对于 dhcp.leases 文件而言,这不会对性能产生太大影响,但对于较大的文件可能会更加麻烦。还有一些需要考虑的事情——如果您的文件系统是异步挂载的,写入会因为write()返回而显得完成,但实际上可能还没有刷新到磁盘上。在这种情况下,重命名可以保护您,sqlite3也可以。
如果您的文件系统是异步挂载的,可能会在数据被写入之前就将其移动。因此,如果您使用的是Unix系统,则最安全的做法可能是挂载同步。不过这已经达到了“如果失败就会有人死亡”的程度。但如果它是一个嵌入式系统,并且它崩溃了,“如果失败就可能失去工作”也是为额外保护提供合理解释的好理由。
ZODB 是一个符合 ACID 标准的数据库存储,主要使用 Python 编写,所以从某种意义上来说答案是肯定的。但我可以想象这可能有点过度杀伤力 :)
要么操作系统为您提供此功能,要么您需要实现自己的 ACID 合规性。例如,通过在编写的文件中定义“记录”,并在打开/读取时验证已写入哪些记录(这可能意味着您需要丢弃一些未完全写入的数据)。例如,ZODB 通过在记录结尾处写入记录本身的大小来实现此目的;如果您可以读取此大小并且匹配,则知道记录已完全写入。
当然,您始终需要追加记录而不是重写整个文件。
在我看来,你的主要目标是确保在断电和系统崩溃的情况下文件的完整性。在这样做时有几个要考虑的事项:
f.flush()
强制将数据写入磁盘,然后使用os.fsync(f.fileno())
。另一种选择是使用sqlite3。
关于我的第二点,我强烈推荐这个演示文稿:http://www.flamingspork.com/talks/2007/06/eat_my_data.odp。它还涵盖了“原子重命名”问题。