我有两个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'使用的一些相同代码 复制请求的目录和文件,然后(假设副本 它取消了原件。如果复制失败,那么部分 已删除复制到目标分区的内容。
重命名整个文件夹也不是原子文件。
如何才能使此暂存过程可靠?
其他一些评论:
答案 0 :(得分:1)
您的命令扩展为:
mv data.db.tmp data.db.tmp-wal data.db data.db-wal
无论如何,多个文件系统操作不是原子操作。
要删除-wal
和-shm
文件,请将日记帐模式更改为DELETE;你以后可以改回来。
一旦有了单个数据库文件,就可以执行原子重命名来替换它。
请注意,WAL模式可以优化写入;将(只读)生产数据库保留为回滚日志模式是个更好的主意。