【问题标题】:Use two FMDB queues (read / write) on the same database在同一个数据库上使用两个 FMDB 队列(读/写)
【发布时间】:2015-07-16 15:23:44
【问题描述】:

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

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

我可以创建两个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..."];
}];

编辑:

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

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

所以我的理解是,我可以创建两个实例使用同一个数据库,但我只需要将其保留在自己的线程上。

【问题讨论】:

  • 您尝试过自己的代码吗?乍一看,这看起来不错
  • 我做到了,而且效果很好。我只是想知道我是否遗漏了什么,或者如果两个队列同时访问同一个数据库是否存在竞争条件。
  • 不要相信我的话,但我很确定只要你不在两个队列上写,你应该没问题。您可以尝试使用 for 循环,例如 1000 次相同的更新语句,同时执行一些读取查询并查看它的反应,以确保确定。但从我记忆中的 FMDB 来看,我认为你可以边写边看
  • 嗯,这不是一个真正的科学方法来找出...:/
  • 统计是数学,数学是一种科学,对吧? :o 哈哈抱歉我不能给你更多我只是尽力而为,但最终我不知道下一个人的那么多

标签: ios objective-c sqlite fmdb


【解决方案1】:

不,您不想让两个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 以确保以不会与@987654328 同时发生的方式进行协调上面的@块:

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

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

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

【讨论】:

  • 感谢 Rob 指出这一点。尽管大部分繁重的工作确实在数据库队列之外。现在回到 Instruments 来衡量这个并问我的用户为什么他们觉得它很慢,我可能也理解错了。修复后我会回来查看的。
  • 好吧,INSERTUPDATE 语句确实需要很长时间。但这与我使用 SQLite FTS 有关。我得调查一下。你回答了我最初的问题,谢谢。
  • 作为记录,在我的 FTS 表中将 UPDATE docs SET contents = ? WHERE sid = ? 替换为 UPDATE docs SET contents = ? WHERE sid MATCH ? 将处理时间从 1257 毫秒更改为... 2 毫秒。
  • @Rob 我可以为所有写操作创建一个单例FMDatabaseQueue。然后直接使用FMDatabase(从不同线程中分离对象)进行SELECT操作。我不在乎何时在后台进行写操作,但是我希望 SELECT 结果立即(即在主线程上同步)
  • @Sam - 遗憾的是,这并不真正奏效,因为您通常不希望在发生写入时发生任何读取。您可以采用读写器模式(其中写入与读取和写入同步)但读取可以同时发生(但仅与其他读取有关)。但是,您必须对 FMDB 进行大量重构才能做到这一点。更简单的是,确保您使用具有写入操作组的事务(以更快地进出),这通常足以获得所需的性能改进。
猜你喜欢
  • 1970-01-01
  • 2016-02-28
  • 1970-01-01
  • 2021-12-24
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多