SQLite 3 WAL模式多个进程经常破坏数据库

时间:2017-09-03 11:54:37

标签: objective-c sqlite wal

我在使用这些标志编译的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下?

我唯一的猜测是,有两个进程写入同一个db是这些损坏实例的根本原因。如果这是真的,我们如何在没有复杂的进程间通信的情况下在两个独立的进程之间进行协调?有任何想法吗?我不想使用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中存在错误或只是我不知道的一些行为。我很乐意切换到readwrite模式并使用PRAGMA query_only如果在锁定方面不会破坏SQLite中的任何其他内容,实际上我的所有读者实际上都是编写者,但从不执行任何修改。

1 个答案:

答案 0 :(得分:0)

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

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