【SQLite 内核】完整性检查与损坏恢复:integrity_check 与修复边界

💡 原文中文,约11700字,阅读约需28分钟。
📝

内容提要

本文介绍SQLite完整性检查与损坏恢复机制。integrity_check检查B-Tree结构自洽性,quick_check跳过UNIQUE和索引一致性检查以提升速度。实测显示,数据字节被翻转后两者均报错,但.recover和.dump只能恢复表结构,无法找回已损坏数据。SQLite对正常崩溃有自动恢复能力,但对绕开正常路径的操作无防御,需靠备份和检查工具主动防护。

🔎

延伸解读

完整性检查的边界:结构自洽 ≠ 数据无损

integrity_check 验证的是 B-Tree 结构自洽性,如页链接、cell 边界、索引与表对应关系,而非逐字节校验内容。一次不破坏结构的字节翻转(如修改 TEXT 值中间字符)可能完全不被发现。要检测此类静默位翻转,需额外使用 cksumvfs 扩展,它会在每页末尾追加校验和。因此,完整性检查通过不能证明数据绝对未被改动。

quick_check 与 integrity_check 的取舍

quick_check 跳过 UNIQUE 约束和索引与表内容一致性检查,复杂度从 O(N log N) 降至 O(N)。若损坏发生在索引与表数据对应关系上(如索引指向不存在的 rowid),quick_check 可能漏报。因此,quick_check 通过不能替代完整性检查,只有完整运行 integrity_check 才能确认索引一致性。

修复工具的局限:.recover 与 .dump 的静默失败

实测显示,当数据字节被翻转后,.recover 和 .dump 都只恢复表结构,数据行全部丢失,且退出码为 0,不报错。.dump 走普通 SELECT 路径,遇到读不出的页会静默跳过,可能被误认为导出成功。官方将此类工具定性为“salvage”(打捞),结果始终存疑,可能包含旧数据复活、值篡改等问题,不能当作原始数据直接使用。

损坏来源与自动恢复的盲区

SQLite 对正常路径内的崩溃(如进程崩溃、断电)有自动恢复能力,但对绕开正常路径的操作(如直接写裸文件、事务中途热拷贝、关闭 journal_mode 或 synchronous、NFS 锁不可靠)无防御。这些情况只能靠 integrity_check 事后发现,且修复工具无法找回已丢失的数据。因此,备份应在损坏发生前建立,而非依赖事后修复。

Q&A

SQLite 的 PRAGMA integrity_check 和 quick_check 有什么区别?

integrity_check 执行完整的低级格式与一致性检查,包括 B-Tree 结构、索引与表内容的一致性以及 UNIQUE 约束,复杂度为 O(N log N)。quick_check 跳过 UNIQUE 约束和索引与表内容一致性的检查,因此速度更快,复杂度为 O(N),但通过 quick_check 不能保证索引与表数据没有脱节。

SQLite 的 integrity_check 能检测出所有类型的损坏吗?

不能。integrity_check 只检查 B-Tree 结构自洽性,如页链接、cell 边界、索引与表的对应关系,不检查 FOREIGN KEY 错误,也不是内容级校验和。如果数据字节被翻转但结构未破坏,integrity_check 可能无法发现。要检测这类静默位翻转,需要额外使用 cksumvfs 扩展。

SQLite 数据库损坏后,.recover 和 .dump 能恢复所有数据吗?

不能。.recover 和 .dump 只能尽力打捞,无法保证完整恢复。实测中,当数据字节被翻转导致内容丢失时,两者都只恢复了表结构,数据行全部丢失,且退出码为 0,不会报错。官方将这类工具定性为 salvage(打捞),结果可能包含丢失内容、旧数据复活、值被篡改等问题。

SQLite 对哪些损坏情况有自动恢复能力?

SQLite 对正常路径内的崩溃(如应用崩溃、断电)有自动恢复能力,通过回放 hot rollback journal 或 WAL 崩溃恢复,在下次访问数据库时自动回滚未完成的事务。但对绕开正常路径的操作,如直接写裸文件、事务中途热拷贝、关闭 journal_mode 或 synchronous 后断电、NFS 锁语义不可靠等,没有自动防御,只能靠 integrity_check 事后检测。

SQLite 官方文档列举了哪些常见的数据库损坏来源?

常见来源包括:应用绕开 SQLite 直接写裸文件、备份或热拷贝在事务中途进行、断电且关闭了保护性 PRAGMA(如 journal_mode=OFF 或 synchronous=OFF)、网络文件系统(如 NFS)锁语义不可靠。这些操作绕开了 SQLite 的正常路径,SQLite 无法防御。

quick_check 通过是否意味着数据库没有结构问题?

不一定。quick_check 跳过 UNIQUE 约束和索引与表内容一致性的检查,如果损坏恰好发生在索引与表数据的对应关系上,quick_check 可能无法发现。只有运行完整的 integrity_check 才能确认这些方面。

SQLite 的 cksumvfs 扩展有什么作用?

cksumvfs 是 SQLite 提供的可选 VFS 扩展,在每个页末尾追加 8 字节校验和,用于检测由随机位翻转引起的数据库损坏。它补充了 integrity_check 无法检测的内容级损坏,因为 integrity_check 只检查结构自洽性,不校验内容。

发现数据库损坏后,应该立即运行 .recover 吗?

不建议立即对生产文件运行 .recover。正确做法是停止继续写入、保留现场、检查是否有可用备份。备份优先于修复,.recover 只是在没有备份时的最后手段,且结果需要人工核对。

🏷️

标签

➡️

继续阅读