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

时间:2015-05-06 13:02:29

标签: ios objective-c sqlite fmdb

我相信我的用例相当普遍,但我找不到这方面的权威答案。

我有一个在后台运行的同步机制,并将数据写入我的数据库。这种同步可能需要很长时间(我使用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..."];
}];

我希望即时获得查询结果(即使未完成同步),也不要等到UPDATE完成。

我可以创建两个FMDatabaseQueue,一个用于写查询,一个用于读取查询?如果读取查询在写入事务中间启动会发生什么?

代码可能如下所示:

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..."];
}];

修改

另外,令我困惑的是文件说明:

  

每个线程制作一个FMDatabase对象一直都可以。只是不要跨线程共享一个实例。

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

1 个答案:

答案 0 :(得分:4)

不,您不希望两个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..."];
    }];
}];

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

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

显然,您应该管理您的后台任务但是适合您的应用程序(我使用了并发操作队列,但您可以使用串行队列或调度队列或任何您想要的)。不要陷入上述backgroundQueue的具体细节,因为您的实施会有所不同。

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