“ Powersafe overwrite”是SQLite团队使用的一个术语,用于描述断电期间与数据保存相关的某些文件系统和磁盘控制器的行为。Powersafe覆盖是一个布尔属性:存储系统拥有或没有。
我们说如果以下语句为真,则系统具有powersafe overwrite属性:
当应用程序在文件中写入一定范围的字节时,即使该写入恰好在崩溃或电源故障之前发生,该范围之外的字节也不会改变。
powersafe overwrite属性没有说明写入的字节的状态。这些字节可能包含其旧值,其新值,随机值或这些值的某种组合。powersafe overwrite属性仅声明写入不能更改超出写入字节范围的字节。
换句话说,电源安全覆盖意味着在写入过程中发生断电时没有“附带损害”。仅实际写入的那些字节可能会损坏。
实际上,powersafe写入属性的意思是,当磁盘控制器检测到即将发生的功率损耗时,它将在停放磁头之前完成其正在处理的任何扇区的写入。这意味着即使断电,单个扇区的写入也将在启动后完成。
考虑如果断电中断磁盘扇区写入会发生什么情况。如果应用程序在某个文件的中间写入两个或三个字节,则操作系统将通过首先读取包含这些字节的整个扇区,对内存中的扇区进行更改,然后将整个扇区写回磁盘来实现此目的。如果在写回过程中发生断电并且未完全写入扇区,则在重新引导后的下一次读取中,该扇区中的纠错代码可能会检测到无法修复的损坏,并且磁盘控制器会将扇区读出为全零或全零。 。因此,值的更改将超出在应用程序级别写入的两个或三个字节的范围,这违反了Powersafe覆盖属性。
的SQLite直至并包括所有版本3.7.9版本 (2011-11-01)假定文件系统并不能提供powersafe覆盖。传统上,SQLite假定文件的任何一个字节更改时,该字节同一扇区内的所有其他字节都有可能因断电而损坏。写入时,SQLite确保确保对任何修改的同一扇区中的所有字节进行日志记录,并将日志文件填充到下一个扇区边界,以便后续添加到该日志记录不会损坏先前的记录。SQLite将扇区大小理解为VFS中xSectorSize方法返回的值。SQLite团队通常将xSectorSize返回的值称为写入的“爆炸半径”,因为它表示在写入过程中发生断电可能损坏的字节范围。Unix和Windows的默认VFS始终返回512,作为所有SQLite版本(包括3.7.9)之前的扇区大小(或爆炸半径)。
但是,较新的磁盘驱动器已开始使用4096字节的扇区。从SQLite版本3.7.10(2012-01-16)开始,SQLite开发团队进行了试验,对xSectorSize进行了更改,以将4096字节报告为爆炸半径。这样的结果是增加了许多数据库的写开销。对于PRAGMA page_size为1024(非常常见的选择)的数据库,现在更改为数据库中的单个页面需要SQLite将其他三个相邻页面备份到回滚日志中,而以前只需要备份一个页面即可。在改变。在WAL模式下,则必须将每个事务填充到WAL文件中的下一个4096字节边界,而不是下一个512字节边界,从而导致每个事务写入数千个额外的字节。
额外的写开销促使人们重新考虑有关电源安全覆盖的假设。对于现代磁盘驱动器,容量已经变得如此之大,数据密度也如此之大,以至于单个扇区非常小,而写入单个扇区只需要很少的时间。我们知道磁盘驱动器可以检测到即将发生的功率损耗,并且可以利用剩余能量继续运行一小段时间,因为这些驱动器可以在旋转之前停下头。因此,如果磁盘控制器可以检测到即将发生的功率损耗,则在停放磁头之前,控制器将在首次检测到即将出现的功率损耗时完成写当前正在工作的任何扇区,这似乎是合理的。不会花太长时间,而对于小而密集的扇区则不应该这样做。因此,假设对现代磁盘进行电源安全覆盖是合理的。确实,有人告诉我们,伯克利数据库已经做了数十年的假设。建议谨慎。正如Roger Binns在SQLite开发人员邮件列表中指出的那样:“'写得不好'应该是关于驱动器固件的主要假设。”
当数据库页面大于磁盘扇区,将数据库页面写入磁盘但在写入数据库页面的所有扇区之前发生断电时,就会发生页面撕裂。然后,在恢复时,数据库页面的一部分将具有旧内容,而页面的另一些部分将具有新内容。一些数据库引擎假定页面写入是原子的,因此页面撕裂是不可恢复的错误。
无论PSOW设置如何,SQLite都不会假定数据库页面写操作是原子的。(1) 因此,SQLite始终能够从崩溃导致的页面撕裂中自动恢复。启用PSOW不会降低SQLite从残缺页面中恢复的能力。
用于SQLite的VFS版本3.7.10(2012-01-16)添加了一个名为SQLITE_IOCAP_POWERSAFE_OVERWRITE的新设备特征。假定报告此特性的数据库文件驻留在具有powersafe overwrite属性的存储系统上。现在,如果使用-DSQLITE_POWERSAFE_OVERWRITE = 1编译SQLite,或者如果使用-DSQLITE_POWERSAFE_OVERWRITE = 0编译,则默认的unix和Windows VFS会报告 SQLITE_IOCAP_POWERSAFE_OVERWRITE, 或者它们假定存储不具有powersafe覆盖属性 。目前,默认情况下将打开电源安全覆盖功能,尽管我们将来可能会重新访问它并将其默认设置为关闭。
当使用带有URI filename的“ psow”查询参数打开数据库时,可以指定各个数据库的powersafe overwrite属性。例如,要始终假定文件的电源安全覆盖(可能是为了确保最大的写入性能),请按以下方式打开该文件:
文件:somefile.db?psow = 1
或为了使数据库更加安全并强制SQLite假定数据库缺少Powersafe覆盖,请使用
文件:somefile.db?psow = 0
sqlite3_file_control()还有一个新的SQLITE_FCNTL_POWERSAFE_OVERWRITE操作码,它允许应用程序查询数据库文件的powersafe覆盖属性。
SQLite永远不会假定原子页面以其默认配置进行写入。但是自定义VFS可以在xDeviceCharacteristic()方法的结果中设置 SQLITE_IOCAP_ATOMIC位之一,然后SQLite将假定页面写入是原子的。应用程序必须提供一个自定义VFS来完成此操作,因为没有一个标准VFS会设置xDeviceCharacteristics()向量中的任何原子位。