关于意外断电的原子写入
Posted
技术标签:
【中文标题】关于意外断电的原子写入【英文标题】:Atomic writes with respect to unexpected power offs 【发布时间】:2016-01-28 11:53:56 【问题描述】:我有一个大约 100KB 长的文件,每隔几分钟就会被一个写入者在 std::ofstream
上使用 operator <<
覆盖。我想避免任何类型的“部分写入”情况,这些情况可能是由于文件被刷新到磁盘时系统关闭而导致的。在 OS/POSIX 允许的范围内,我想在我的软件中执行此操作。
我的想法是使用overwrite-by-rename策略,即flush()
将所有数据改成一个临时文件名,然后将临时文件名重命名为最终文件名。
我的问题是这是否是一个好的策略,从某种意义上说,重命名的原子性由我的 POSIX 操作系统(例如 Linux)保证,或者您有什么更好的想法(不涉及硬件修改,也可能不涉及 FS 标志修改在内核/系统级别)
【问题讨论】:
你看过RDBMS软件是如何处理这个案例的吗? @zaratustra,谢谢你提醒我。我确实知道像 SQLite 这样的 RDBMS 可以缓解这些问题,我们确实在其他地方使用它,但由于历史和最初的糟糕设计原因,我们现在必须使用文件。 我想底层文件系统的事务/日志性质将是这里最大的因素。但这个策略似乎很好(UPS 失败)。您可以将其扩展为三阶段提交;首先写入新的临时文件,重命名旧文件,重命名新的临时文件(然后可选,虽然可能最好,删除旧的重命名文件)。 【参考方案1】:如果您不信任重命名的原子性 - 并且在非日志文件系统崩溃的情况下您不能 - 那么您可以先用临时名称重命名原始文件,然后重命名新文件到原始名称,然后才使用临时名称删除原始文件。这样,如果发生崩溃,三个文件路径中的至少一个当前不会被修改。
【讨论】:
但是在原始名称重命名为临时名称之后(但在新文件重命名为原始名称之前)可能会发生崩溃。这将导致我没有最终文件 为什么我不能依赖重命名的原子性以防崩溃? @Martin 但是在原始名称重命名为临时名称之后(但在新文件重命名为原始名称之前)可能会发生崩溃。这将导致我没有最终文件 那么?如果您找到该状态,您就会知道系统出现故障时您所处的状态,并且恢复起来很容易。 @Martin。此答案中的此策略允许您检查三个文件是否存在,因此您将能够通过比较推断出是否发生崩溃,哪个文件是最新文件,哪个文件是旧文件. @Martin 有些文件系统无法保证抗崩溃性。在覆盖重命名期间可能会发生崩溃,两个文件路径都处于无效状态。【参考方案2】:一般来说,这是您可以实现的最佳方式。从应用程序的角度来看,它将充当原子操作。
但是,您仍然需要在操作系统中获得一些支持,即日志文件系统。只要您使用 ext4、reiser、zfs、xfs 或任何其他现代文件系统,此解决方案就会以正确的顺序将数据刷新到底层硬件。但对于 fat/vfat,它可能不起作用。
【讨论】:
为了防止发生电源故障时的损坏,实际上可能需要硬件支持,例如不间断电源或存储介质上的电池/电容器。以上是关于关于意外断电的原子写入的主要内容,如果未能解决你的问题,请参考以下文章
服务器数据恢复HP EVA服务器存储意外断电导致RAID信息丢失的数据恢复案例