WAL 模式下的 SQLite 备份:为什么不能直接 cp

2026-11-09 · 数据库

SQLite 单文件、零运维,很适合小型服务。但它的备份并不像看起来那样「复制一个文件就行」。开了 WAL(预写日志)模式之后,直接 cp 活库,有很大概率拿到一份不一致的副本——而且当时不会报错,等真的需要恢复时才发现。

WAL 模式下数据在哪里

WAL 模式把改动先写进 xxx.db-wal,再在检查点(checkpoint)时合并回主库文件。也就是说,某一时刻的完整数据 = 主库文件 + WAL 文件(可能还有共享内存索引 -shm),三者必须处于同一时刻的一致状态。

直接 cp xxx.db backup.db 只复制了主库,WAL 里的最新改动全部丢失;更糟的是,如果恰好赶上检查点正在写回,主库本身也可能是半写的。

正确做法

让 SQLite 自己来做,用 .backup 命令(它会正确处理锁与 WAL):

sqlite3 /path/to/live.db ".backup '/tmp/snapshot-$(date +%s).db'"

然后校验这份快照是真的能用,而不是只看文件大小:

sqlite3 /tmp/snapshot-*.db "pragma integrity_check;"
sqlite3 /tmp/snapshot-*.db "select count(*) from your_table;"

期望第一条返回 ok,第二条返回你预期行数。两条都过了,才算备份成立。

三个容易忽略的点

一个判断标准

如果你判断一份备份是否可用的唯一依据是「文件存在且体积正常」,那它随时可能变成一份看起来很对的废文件。可信的备份至少要回答两件事:能打开、内容符合预期。