SQLite 开启 WAL 后,怎样验证备份真的包含最新数据

举报
yd_271400591 发表于 2026/11/11 19:41:34 2026/11/11
【摘要】 用 Python 标准库复现 SQLite WAL 备份丢失最新记录的场景,并比较直接复制与在线备份。示例说明完整性检查与业务数据核对各自能验证什么。

把一个正在运行的 SQLite 数据库复制到备份目录,副本可能能正常打开,甚至能通过完整性检查,却少了最近提交的数据。开启 WAL 后,已提交的变更可以暂存在 数据库名-wal 文件里,等检查点再写回主文件。只复制主文件,可能拿到一个结构完好但已经过时的副本。

下面用 Python 标准库在临时目录复现这个现象。先把第一条记录写回主文件,再提交第二条记录,同时让源连接保持打开。对比文件复制与 SQLite 在线备份的结果。

import shutil
import sqlite3
from contextlib import closing
from pathlib import Path
from tempfile import TemporaryDirectory


def inspect(path):
    uri = path.as_uri() + "?mode=ro"
    with closing(sqlite3.connect(uri, uri=True)) as db:
        check = db.execute("PRAGMA integrity_check").fetchone()[0]
        rows = db.execute("SELECT id FROM events ORDER BY id").fetchall()
        return check, [row[0] for row in rows]


with TemporaryDirectory() as folder:
    root = Path(folder)
    source = root / "live.db"
    copied = root / "copied.db"
    backup = root / "backup.db"

    with closing(sqlite3.connect(source)) as db:
        mode = db.execute("PRAGMA journal_mode=WAL").fetchone()[0]
        assert mode == "wal"
        db.execute("PRAGMA wal_autocheckpoint=0")
        db.execute("CREATE TABLE events (id INTEGER PRIMARY KEY)")
        db.execute("INSERT INTO events VALUES (1)")
        db.commit()
        db.execute("PRAGMA wal_checkpoint(TRUNCATE)").fetchone()

        db.execute("INSERT INTO events VALUES (2)")
        db.commit()

        shutil.copyfile(source, copied)
        with closing(sqlite3.connect(backup)) as destination:
            db.backup(destination)

        print("只复制主文件", inspect(copied))
        print("SQLite 在线备份", inspect(backup))
只复制主文件 ('ok', [1])
SQLite 在线备份 ('ok', [1, 2])

两个副本都返回 ok,因为 integrity_check 检查的是数据库内部结构。它不会替你核对备份应包含哪批业务数据。这也是恢复演练时要再查一条业务指标的原因,例如备份时已确认提交的批次号,或一组带时间戳的记录。示例关闭自动检查点并保持源连接打开,只是为了让这个现象稳定出现。真实应用无需修改这些设置来做备份。

实际使用时,让 Connection.backup() 写入新的目标文件,关闭目标连接后再验证。源库可以用 mode=ro 打开,这样路径写错时会报错,不会意外创建一个空库。先对副本执行完整性检查,再抽查关键业务记录。存在外键时,可另外执行 PRAGMA foreign_key_check。每一步都通过后,再将副本移到备份目录并记录完成时间。

如果应用一直有新写入,事后比较源库与副本的总行数容易误报。可以改用备份前已提交且之后不会变化的记录作为基准。恢复时先在独立目录打开备份,让应用执行一次只读查询,确认实际读取到预期记录,再安排正式切换。

参考资料为 SQLite 官方文档的《SQLite Backup API》《Write-Ahead Logging》,以及 Python 标准库文档 sqlite3 中 Connection.backup() 一节。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。