【SQLite 内核】在线备份与 sqlite3_backup:与 cp 热拷贝的语义差
内容提要
本文介绍SQLite在线备份机制。sqlite3_backup API通过pager读取页面,可分批执行并释放锁,生成一致快照;而cp热拷贝依赖内部写入时序,可能产生损坏。WAL模式下需连同-wal文件一起备份。实测验证了cp在写事务中拷贝会得到物理大小与逻辑页数不一致的副本。备份后应运行integrity_check确认完整性。
延伸解读
cp 热拷贝的“运气”本质
实测显示,在写事务期间 cp 主文件,可能得到物理大小与逻辑页数不一致的副本,但 integrity_check 仍通过,因为恰好落在“叶子页已溢写、头部未更新”的窗口。这并非安全,而是运气。若时序相反,则可能得到新旧混杂的损坏文件。cp 的正确性完全依赖内部写入顺序,调用者无法控制,因此不应作为备份手段。
WAL 备份的隐藏陷阱
WAL 模式下,未 checkpoint 的已提交事务只存在于 -wal 文件,只拷主文件会丢失这些数据。官方明确要求 -wal 或 -journal 必须与主文件一起拷贝。backup API 通过 pager 读取,自动处理 WAL 帧,无此问题。另外,WAL 模式下无法通过 backup API 修改 page_size,需先切回 rollback journal 模式。
增量备份的缺失与代价
backup API 每次都是全量拷贝,没有增量或差量备份。对于大数据库,频繁备份的 I/O 成本高。官方工具 sqlite3_rsync 只解决远程传输效率,不解决本地增量问题。SQLite 目前没有基于 WAL 帧的增量备份机制,应用层需自行设计,例如基于变更追踪 API。
Q&A
SQLite 的 sqlite3_backup API 和直接 cp 数据库文件有什么区别?
sqlite3_backup API 通过 pager 读取页面,能生成一致的快照,且可分批执行并释放锁;而 cp 热拷贝依赖内部写入时序,可能产生损坏。
在 WAL 模式下备份 SQLite 数据库时,需要注意什么?
必须连同 -wal 文件一起备份,否则会丢失未 checkpoint 的已提交事务。
sqlite3_backup API 的三个函数分别有什么作用?
sqlite3_backup_init 创建备份对象,sqlite3_backup_step 拷贝指定页数,sqlite3_backup_finish 释放资源。
为什么 cp 热拷贝 SQLite 数据库文件可能得到损坏的副本?
因为 cp 拷贝的是原始字节,其正确性依赖于 SQLite 内部脏页刷盘的时序,如果拷贝发生在事务中间,可能得到新旧内容混杂的文件。
VACUUM INTO 和 sqlite3_backup 有什么区别?
VACUUM INTO 会顺带重建页面、清理空闲页并重排表和索引,而 sqlite3_backup 只是原样快照,不整理碎片。
备份完成后为什么要运行 integrity_check?
为了确认备份得到的文件是结构自洽的快照,而不是默认信任命令没报错就等于备份正确。
SQLite 是否支持增量备份?
目前没有官方的增量备份 API,每次备份都是全量拷贝。