【问题标题】:SQLite 3 WAL mode multiple processes frequently corrupting databaseSQLite 3 WAL模式多进程频繁破坏数据库
【发布时间】:2018-02-11 20:59:03
【问题描述】:

我在使用这些标志编译的 WAL 模式下使用 SQLite 3:

#define SQLITE_ENABLE_FTS3 1
#define SQLITE_THREADSAFE 2
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_ENABLE_STAT4 1
#define SQLITE_MAX_MMAP_SIZE 0
#define SQLITE_OMIT_DEPRECATED 1
#define SQLITE_OMIT_SHARED_CACHE 1
#define SQLITE_OMIT_AUTOMATIC_INDEX 1

我有一个 Mac 应用程序,其中包含一堆插件(每个插件都在自己的进程中)随机访问数据库并进行修改。所有进程都链接到使用自定义 SQLite 代码构建的同一个共享库。文档说多个进程可以随时读取,但实际上只有一个进程可以进行修改。这是否意味着我需要以某种方式编排和协调进程之间的写入

我之所以问,是因为自从我采用 WAL 以来,我经常看到有关格式错误的数据库的报告。这似乎主要发生在一个进程在打开连接后崩溃(不管它是否使用SQLITE_OPEN_READONLY 标志打开)并且另一个进程已经打开了一个连接或稍后打开了一个连接。我不能总是可靠地重现这一点,但它似乎与一个进程创建的 -shm 和 -wal 索引文件有关,而另一个进程要么在内存中有自己的副本,要么产生了一些不匹配不知何故。不应该是这种情况,但可能是新进程使用的 -shm 文件以某种方式修改它(或 .db 文件)而没有第一个进程发现,从而导致数据库损坏(如前所述在如何破坏 SQLite 的 2.4 下)?

我唯一的猜测是让两个进程写入同一个数据库是这些损坏实例的根本原因。如果这是真的,我们如何在没有复杂的进程间通信的情况下在两个独立的进程之间进行协调?有任何想法吗?我不想求助于使用journal_mode=DELETE,因为我有一个高度多线程的应用程序,否则它会受益于并发读取器和单个写入器。

顺便说一下,我是这样打开一个阅读器的(多个阅读器同时被多个线程打开):

NSString *path = ...

sqlite3 *readOnlyDB = NULL;

BOOL dbOpened = (sqlite3_open_v2(path.UTF8String, &readOnlyDB, SQLITE_OPEN_READONLY | SQLITE_OPEN_WAL, NULL) == SQLITE_OK);

sqlite3_exec(readOnlyDB, "PRAGMA read_uncommitted=1; PRAGMA query_only=1; PRAGMA synchronous=normal;", NULL, NULL, NULL);
sqlite3_unicode_init(readOnlyDB);
sqlite3_busy_timeout(readOnlyDB, 2000)

// I register custom functions here for the connection

这就是我打开写入器的方式(单个写入器存在于单个进程中):

BOOL dbOpened = (sqlite3_open_v2(path.UTF8String, &dbConnection, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK);

sqlite3_exec(dbConnection, "PRAGMA main.journal_mode=WAL; PRAGMA synchronous=normal;", NULL, NULL, NULL);
sqlite3_unicode_init(dbConnection);
sqlite3_busy_timeout(dbConnection, 2000);

// I register custom functions here for the connection

在关闭作家之前,我总是运行以下检查点:

  ....
  sqlite3_exec(dbConnection, "PRAGMA wal_checkpoint(PASSIVE)", NULL, NULL, NULL);

编辑:我发现了另一个奇怪的行为,不确定这是否相关。如果我用SQLITE_OPEN_READWRITE 而不是SQLITE_OPEN_READONLY 打开我的阅读器,则在连接上调用sqlite3_close_v2(...) 后,-shm 和-wal 文件将被正确删除。如果我切换回SQLITE_OPEN_READONLY,我会看到 -shm 和 -wal 文件永远不会被删除,即使进程完全关闭也是如此。我在我的一个插件中使用只读模式(这似乎主要导致损坏,特别是在它退出或使用插件的主机应用程序崩溃等情况下),我看到了这种行为变化。我想知道这是否表明 SQLite 中存在错误,或者只是我不知道的一些行为。我很乐意切换到读写模式并使用PRAGMA query_only,如果这不会破坏 SQLite 中的任何其他锁定方面的内容,因为实际上我的所有读者实际上都是作家,但从不进行任何修改。

【问题讨论】:

  • 哪个文件系统?涉及到什么网络?虚拟机?
  • @CL。我用一些新的观察更新了这篇文章。 macOS 10.12,没有网络,没有虚拟机。所有用户都使用 macOS 10.10+。这是一个应用程序 + Today 小部件 + Action 插件 + Share 插件,全部使用共享框架,但在各自的进程中。
  • docs
  • @CL。谢谢,我有好几次了,我想我正在按部就班地做所有事情。我关于使用 SQLITE_OPEN_READONLY 时没有删除 -shm 和 -wal 文件的观点 - 我怀疑这是主要问题。我已经切换到完全使用 SQLITE_OPEN_READWRITE 并解决了这个问题。通过此更改,我也无法再重现数据库的损坏,所以我认为现在我会坚持这一点。我仍然认为 SQLite 中的 SQLITE_OPEN_READONLY + Wal 存在一些错误。
  • 链接说不能以只读方式打开 WAL 数据库。

标签: objective-c sqlite wal


【解决方案1】:

我在 Linux 上使用 Python 客户端的 SQLite3 遇到了类似的问题:当客户端在打开的事务中崩溃时,DB 文件格式错误,而另一个并发进程执行读/写查询。

在我添加异常处理程序以确保事务在崩溃进程终止之前关闭后,该问题得到解决。

【讨论】:

    猜你喜欢
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 2012-08-13
    相关资源
    最近更新 更多