【问题标题】:Staging SQLite Database Files Reliably可靠地暂存 SQLite 数据库文件
【发布时间】:2013-10-18 11:00:52
【问题描述】:

我有两个 SQLite 数据库文件:

  • data.db生产
  • data.db.tmp暂存

两个数据库都在WAL journaling mode。此外,暂存数据库处于独占锁定模式(使用PRAGMA locking_mode),只有一个读取器/写入器,而生产数据库处于共享/正常锁定模式,有多个读取器,没有写入器。

在任何给定时间点,文件结构可能如下所示:

  • data.db
  • data.db-shm
  • data.db-wal
  • data.db.tmp
  • data.db.tmp-wal

有时,我想用临时数据库替换生产数据库 - 最好不要破坏现有的 [生产] 阅读器,更重要的是,不要损坏数据库。

我最初(幼稚)的想法是一个简单的mv data.db-tmp data.db,但是因为有多个相关文件,单个重命名并不能保证原子性和一致性。然后我想到了做一个支撑mv

mv data.db{.tmp,.tmp-wal} data{.db,.db-wal}

我不知道上述是否是原子操作,但考虑到 *-shm*-wal 文件的瞬态性质,它不会很好地工作:如果 data.db.tmp-wal 不存在,则移动将失败(我认为!)并且可能存在的 data.db-shm 对应对象没有原子操作。

根据info coreutils 'mv invocation'

在fileutils '4.0' 版本之前,'mv' 只能移动常规 文件系统之间的文件。例如,现在 'mv' 可以移动整个 目录层次结构,包括来自一个分区的特殊设备文件 给另一个。它首先使用“cp -a”使用的一些相同代码 复制请求的目录和文件,然后(假设复制 成功)它会删除原件。如果复制失败,则该部分 复制到目标分区的文件被删除。

重命名整个文件夹也不是原子的。

我可以做些什么来使这个暂存过程可靠?


一些补充说明:

  • 我的客户端 API 是 PHP/PDO,所以我无法访问 SQLite online backup C interface
  • 数据库大小为数 GB,因此某些内存解决方案可能不可行

【问题讨论】:

    标签: php sqlite atomic corruption


    【解决方案1】:

    您的命令扩展为:

    mv data.db.tmp data.db.tmp-wal data.db data.db-wal
    

    无论如何,多个文件系统操作都不是原子的。


    要删除-wal-shm 文件,请将日志模式更改为DELETE;你可以稍后再改回来。 一旦有了单个数据库文件,就可以执行原子重命名来替换它。

    请注意,WAL 模式针对写入进行了优化;最好让(只读)生产数据库处于回滚日志模式。

    【讨论】:

    • 这件事发生在我身上,但我不确定它会这么简单。将暂存数据库上的日志模式更改为 DELETE 是否足以让生产连接检测到它不再需要 -wal-shm 文件?
    • 我担心的是,如果我需要将 production 数据库的日志模式也更改为 DELETE,读者可能会连接到它并因此更改日志模式再次回到 WAL(在 mv 发生之前)。解决此问题的一种方法是将两个数据库上的锁定模式设置为独占,但这会扰乱未来的读者,并且 - 我不确定 - 如果在特定时间有任何具有共享锁的活动读者可能会失败。
    • 活跃的读者会继续从旧文件中读取;他们只会在重新打开新文件时看到新文件,即使用新的数据库连接。
    猜你喜欢
    • 1970-01-01
    • 2021-06-05
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多