在线文档教程
Sqlite
其他 | Miscellaneous

Powersafe Overwrite

Powersafe Overwrite

“Powersafe覆盖”是SQLite团队用来描述某些文件系统和磁盘控制器在断电期间与数据保存相关的行为的术语。Powersafe覆盖是一个布尔属性:存储系统拥有它或不存在。

如果以下语句成立,我们说系统具有powersafe覆盖属性:

When an application writes a range of bytes in a file, no bytes outside of that range will change, even if the write occurs just before a crash or power failure.

powersafe覆盖属性没有说明写入的字节的状态。这些字节可能包含旧值,新值,随机值或这些值的组合。powersafe覆盖属性仅仅表明写入不能改变写入字节范围之外的字节。

换句话说,动力安全覆盖意味着在书写时发生断电时不会有“附带损害”。只有实际写入的字节可能会被损坏。

实际上,powersafe写属性的意思是,当磁盘控制器检测到即将发生的功率损耗时,它会在停放磁头之前完成正在处理的任何扇区。这意味着即使存在电力损耗,单个扇区写入也会一次完成。

考虑一下如果磁盘扇区写入被断电中断会发生什么。如果应用程序在某个文件的中间写入两个或三个字节,操作系统将通过首先读取包含这些字节的整个扇区,在内存中更改扇区,然后将整个扇区写回到磁盘来实现此目的。如果在回写过程中发生断电并且扇区未完全写入,则在重新启动后的下一次读取时,扇区中的纠错码可能会检测到无法挽回的损坏,并且磁盘控制器将读出该扇区为全零或全部为零。因此,值将在应用程序级别写入的两个或三个字节范围之外发生更改 - 这违反了powersafe覆盖属性。

SQLite Assumptions About Powersafe Overwrite

所有版本的SQLite直到并包括版本3.7.9(2011-11-01)假定文件系统不提供powersafe覆盖。SQLite传统上认为,当一个文件的任何一个字节发生变化时,该字节同一扇区内的所有其他字节都有可能在功率损失时被破坏。写入时,SQLite确保将任何修改的相同扇区中的所有字节记入日志文件,并将日志文件填充到下一个扇区边界,以便随后追加到该日志中不会损坏先前的记录。SQLite将扇区大小理解为VFS中xSectorSize方法返回的值。SQLite团队经常将xSectorSize返回的值作为写入的“爆炸半径”,因为它表示在写入期间发生掉电时可能损坏的字节范围。

较新的磁盘驱动器已开始使用4096字节的扇区。从SQLite 版本3.7.10(2012-01-16)开始,SQLite开发团队尝试更改xSectorSize以报告4096字节作为爆炸半径。这会增加许多数据库的写入开销。对于PRAGMA page_size为1024的数据库(非常普遍的选择),对数据库中的单个页面进行更改现在需要SQLite将其他三个相邻页面备份到回滚日志中,而以前它只需要备份一个页面正在改变。在WAL模式下,每个事务都必须填充到WAL文件中的下一个4096字节边界,而不是下一个512字节边界,从而导致每个事务写入数千个额外的字节。

额外的写入开销促使人们重新审视有关powersafe覆盖的假设。使用现代化的磁盘驱动器,容量变得如此之大,数据密度如此之大以至于单个扇区很小,编写单个扇区所需的时间很少。我们知道,磁盘驱动器可以检测即将发生的功率损耗,并在剩余能量上继续运行一段时间,因为这些驱动器能够在停转之前停放磁头。因此,如果磁盘控制器检测到即将发生的功率损耗,控制器在停止磁头之前首次检测到即将发生的功率损耗时将完成当前正在工作的任何扇区的写入是合理的,只要这样做不会太长,这不应该与小而密集的部门。因此,假设powersafe覆盖现代磁盘似乎是合理的。事实上,我们被告知,伯克利已经做了几十年的这个假设。尽管谨慎,正如Roger Binns在SQLite开发人员邮件列表中指出的那样:“'写得不好'应该是驱动器固件的主要假设。”

Torn Pages

当数据库页面大于磁盘扇区时,会出现破损页面,数据库页面会写入磁盘,但在写入数据库页面的所有扇区之前会发生断电。然后,恢复时,部分数据库页面将包含旧内容,而页面的其他部分将包含新内容。一些数据库引擎假设页面写入是原子的,因此页面破损是不可恢复的错误。

SQLite从不假定数据库页面写入是原子的,不管PSOW的设置如何。(1)因此,SQLite总是能够从崩溃导致的破损页面中自动恢复。启用PSOW不会降低SQLite从破损页面恢复的能力。

Changes In SQLite Version 3.7.10

SQLite 版本3.7.10(2012-01-16)的VFS 添加了名为SQLITE_IOCAP_POWERSAFE_OVERWRITE的新设备特征。报告此特征的数据库文件假定驻留在具有powersafe覆盖属性的存储系统上。如果使用-DSQLITE_POWERSAFE_OVERWRITE = 1编译SQLite,则默认的unix和windows VFS现在报告SQLITE_IOCAP_POWERSAFE_OVERWRITE,或者如果使用-DSQLITE_POWERSAFE_OVERWRITE = 0进行编译,他们会假定存储没有powersafe覆盖属性。目前,默认情况下powersafe覆盖是打开的,虽然我们可能会在将来重新访问并将其默认关闭。

可以指定单个数据库的powersafe覆盖属性,因为使用具有URI文件名的“psow”查询参数打开数据库。例如,要始终假定文件的powersafe覆盖(可能为了确保最大写入性能),请将其打开为

file:somefile.db?psow=1

或者为了使数据库更安全,并强制SQLite假定数据库缺少powersafe覆盖,请使用它打开它

file:somefile.db?psow=0

对于sqlite3_file_control(),还有一个新的SQLITE_FCNTL_POWERSAFE_OVERWRITE操作码,它允许应用程序查询数据库文件的powersafe覆盖属性。

Notes:

  • SQLite从不会假定其默认配置中的原子页面写入。但是自定义VFS可以在xDeviceCharacteristic()方法的结果中设置SQLITE_IOCAP_ATOMIC位中的一个位,然后SQLite将假定页写入是原子的。但是,应用程序必须提供自定义VFS才能完成此操作,因为没有任何标准VFS将设置xDeviceCharacteristics()向量中的任何原子位。

SQLite is in the Public Domain.