在同一个数据库上使用两个 FMDB 队列(读/写)

Jon*_*nas 4 sqlite objective-c fmdb ios

我相信我的用例相当普遍,但我找不到对此的权威答案。

我有一个在后台运行并将数据写入我的数据库的同步机制。这种同步可能需要很长时间(我使用 FTS)。为此,我使用FMDatabaseQueue. 当我想读取数据库时,我使用相同的队列进行查询。

现在,当同步进程已经将大量事务排队到队列中时,应用程序想要进行读取,它必须等待所有写入事务完成才能进行查询,因为这是一个串行队列。代码可能如下所示:

FMDatabaseQueue *queue = [self getDatabaseQueue];

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[queue inDatabase:^(FMDatabase *db) {
    FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
Run Code Online (Sandbox Code Playgroud)

我想立即获得查询结果(即使同步未完成),而不是等待UPDATE完成。

我可以创建两个FMDatabaseQueues,一个用于写查询,一个用于读查询?如果读查询在写事务的中间开始会发生什么?

代码可能如下所示:

FMDatabaseQueue *writeQueue = [self getWriteDatabaseQueue];
FMDatabaseQueue *readQueue = [self getReadDatabaseQueue];

[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[writeQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    // Very slow process
    [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
}];

[readQueue inDatabase:^(FMDatabase *db) {
    FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
}];
Run Code Online (Sandbox Code Playgroud)

编辑:

此外,让我感到困惑的是文档说明:

每个线程都可以创建一个 FMDatabase 对象。只是不要跨线程共享单个实例。

所以我从中了解到的是,我可以使用相同的数据库创建两个实例但我只需要将它保留在自己的线程上。

Rob*_*Rob 6

不,您不想让两个FMDatabaseQueue与同一个数据库交互。整个目的FMDatabaseQueue是提供一个队列,用于通过共享的单个串行队列协调来自不同线程的数据库调用。

不过,我想知道您在“非常慢的进程”块中的代码。如果这不是特定于数据库的东西,那么它不应该在inTransaction和/或inDatabase调用中。

NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init]; 
FMDatabaseQueue *databaseQueue = [FMDatabaseQueue databaseWithPath:path]; 

[backgroundQueue addOperationWithBlock:^{
    // very slow process

    [databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
    }];
}];

[backgroundQueue addOperationWithBlock:^{
    // very slow process

    [databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        [db executeUpdate:@"UPDATE docs SET name = ?", "value..."];
    }];
}];
Run Code Online (Sandbox Code Playgroud)

当那些排队并运行时,主队列可以进行自己的databaseQueue调用,使用FMDatabaseQueue来确保以这种方式进行协调,使其不会与上述inTransaction块同时发生:

[databaseQueue inDatabase:^(FMDatabase *db) {
    FMResultSet *resultSet = [db executeQuery:@"SELECT name..."];
    while (![resultSet next]) {
        ....
    }
}];
Run Code Online (Sandbox Code Playgroud)

显然,你应该管理你的后台任务,但它适合你的应用程序(我使用了并发操作队列,但你可以使用串行队列或调度队列或任何你想要的)。不要被backgroundQueue上面的细节所困扰,因为您的实现会有所不同。

关键的观察是您不应该使用databaseQueue来管理您的“非常慢的进程”任务以及数据库交互。从inTransactionandinDatabase调用中取出与数据库无关的任何内容。使用您自己的队列来管理您自己的非数据库相关代码。始终databaseQueue尽可能快地进出,并让单个共享FMDatabaseQueue协调与来自多个线程的数据库的交互。