科技 · 实用指南
SQLite 备份完成就够了吗?轮换前重新打开并验证快照
备份调用完成只证明复制过程结束,不代表候选文件符合恢复约定。重新只读打开准确文件,执行 SQLite 与应用检查,安全提升后再轮换旧的可靠快照。

SQLite 备份调用成功只是恢复验收的起点。先写入唯一候选路径,正确关闭目标,再只读打开这个准确文件,避免检查误落到在线数据库。
验收既需要引擎级证据,也需要应用级证据。仅在所有检查都通过后才在目标文件系统上提升候选,并保留上一份可靠备份,直到提升后的文件再次打开成功。
1. 创建候选,而不是最终轮换目标
源数据库继续使用或需要进度与分页时,使用 SQLite 在线备份 API;Python 通过 Connection.backup() 提供它。VACUUM INTO 也能生成一致的在线紧凑快照,但输出必须是新文件或空文件,中断时仍可能留下不完整或损坏的文件。
把候选写入将要提升的目录。活动 WAL 数据库不应默认只复制主 .db 文件;若流程必须复制文件,就要主动静止数据库或处理全部必需的伴随文件。
2. 只读重新打开准确候选
关闭目标连接后,以只读 URI 打开候选本身。不要在源连接上运行验收查询,也不要使用可能指向在线库的路径别名。提升前记录路径、大小、哈希、SQLite 与 Python 版本、创建方法和结果。
在 CPython 3.12.14、SQLite 3.53.1 的 Linux 实验中,Connection.backup() 候选可只读打开;提升后又单独重开目标路径。这个边界能发现内存成功标志看不到的路径错误。
3. 组合结构、外键与应用检查
要求 PRAGMA quick_check 只返回一行 ok,或者在时间允许时运行更完整的 integrity_check。两者都不验证外键;SQLite 明确说明 integrity_check 不发现外键错误,因此还要单独运行 foreign_key_check 并要求空结果。
随后验证应用真正需要的恢复约定:user_version 或迁移表、必需表和索引、已知哨兵记录、关键计数或不变量及代表性读取。四行实验得到 quick_check=ok、无外键行和 sentinel=(sentinel, 3);破坏前16字节的副本在重开时被拒绝。
4. 先提升,最后轮换旧副本
任何检查失败都要让候选留在轮换集之外,保留上一份可靠备份,并记录准确失败查询。不要原地修复候选后宣称通过;重新创建快照才能保持来源清楚。结构检查通过也不证明业务数据完整、最新或语义正确。
全部通过后,用同一文件系统的重命名或替换进行提升。Python 说明 os.replace 成功时具有原子性,但跨文件系统可能失败。删除或老化旧备份前,再打开提升后的路径并重复最小哨兵检查,同时定期在隔离环境验证完整恢复流程。
