ios - 在多线程和两个连接上使用 FMDB

标签 ios multithreading fmdb

我在我的应用中使用了两种不同类型的 fmdb 连接:

所有 READ 查询的 FMDatabase 和 FMDatabaseQueue 用于所有 UPDATE 查询。

两者都由单例处理,在应用程序运行期间,这两种类型都保持打开状态。

读取和更新查询都在不同的线程中使用,因为我的应用程序中的某些任务在后台进行;比如从服务器获取数据并在自己的后台线程中通过 FMDatabaseQueue 将其插入数据库 - 同时通过 FMDatabase 从数据库读取一些信息并在主线程上用它更新 ViewController。

我的问题是,在通过 FMDatabaseQueue 将数据插入数据库后,第二个连接 (FMDatabase) 没有返回更新的信息,因为它没有找到它们。但是我知道数据已插入,因为我已经使用数据库浏览器工具检查了数据库+插入时没有发生错误。为避免这种情况,我必须关闭 FMDatabase 数据库连接并重新打开它以查看其他连接所做的更改。不幸的是,当我的应用程序启动时,有很多插入、更新和读取,因为从服务器加载了大量需要处理的新数据——所以每次更新时关闭和打开数据库都会发生在许多“数据库繁忙”中消息。

我之前为所有线程使用了一个单一的 FMDatabaseQueue 并执行(读取,更新)但是当使用带有 __block 变量的读取查询从回调中获取结果集时它非常慢,而另一个线程执行一些插入(在 50-单笔交易 100 个)。

最重要的是,数据库是通过 sqlcipher 加密的——不确定它是否重要,但想提一下。因此,每次我必须关闭和打开数据库时,我都会执行 setKey。

我的问题:是否可以在多个线程上使用具有两种不同连接类型的设置?如果可以,我是否必须关闭和打开 FMDatabase 连接?或者对于这个用例是否有更好的解决方案?

更新

我执行插入/更新的代码看起来像

-(void) create:(NSArray *)transactions
{
    NSMutableString *sqlQuery = [[NSMutableString alloc] initWithString:STANDARD_INSERT_QUERY];

    [sqlQuery appendString:@"(transaction_id, name, date) VALUES (?,?,?)"];

    FMDBDataSource *ds = [FMDBDataSource sharedManager];
    FMDatabaseQueue *queue = [ds getFMDBQ];
    [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        [db setKey:[ds getKey]]; // returns the key to decrypt the database
        for (Transaction *transaction in transactions)
        {
            [db executeUpdate:sqlQuery, transaction.transactionId, transaction.name, transaction.date];
        }
    }];
}

和一个读取查询

-(Transaction *)read:(NSString *)transactionId
{
    NSString *sqlQuery = [[NSString alloc] initWithString:STANDARD_SELECT_QUERY];
    Transaction *transaction = nil;

    FMDBDataSource *ds = [FMDBDataSource sharedManager];
    FMResultSet *rs = [[ds getFMDB] executeQuery:sqlQuery];

    while ([rs next]) {
        transaction = [[Transaction alloc] init];
        [transaction setTransactionId:[rs stringForColumn:@"transaction_id"]];
        [transaction setName:[rs stringForColumn:@"name"]];
    }

[rs close];
return transaction;
}

FMDBDataSource 是一个包含 FMDatabase 和 FMDatabaseQueue 连接的单例

- (FMDatabaseQueue *)getFMDBQ
{
    if (self.fmdbq == nil)
    {
        self.fmdbq = [FMDatabaseQueue databaseQueueWithPath:[self getDBPath]];
    }

    return self.fmdbq;
}

- (FMDatabase *) getFMDB
{
    if(self.fmdb == nil)
    {
        self.fmdb = [FMDatabase databaseWithPath:[self getDBPath]];
        [self openAndKeyDatabase]; // opens the db and sets the key as the db is encrypted
    }
    return self.fmdb;
}

正如我所说,当使用此代码时,FMDatabase 连接不会获取通过 FMDatabaseQueue 插入的信息。

最佳答案

就个人而言,我建议使用单个 FMDatabaseQueue对于两个线程,让队列协调两个线程上的操作。这就是它的创建目的。它完全消除了那些“数据库繁忙”的问题。

关于性能更新,如果进行批量更新,您是否使用了 FMDatabase 方法 beginTransaction更新前和commit在最后?或者使用 inTransaction 方法。在我的测试中插入 10,000 条没有事务的记录需要 36.8 秒,但有事务则需要 0.25 秒。

或者,如果您的批量更新速度很慢(例如,您正在使用某种流式传输协议(protocol)从 Web 服务下载一些大数据源),您可以:

  • 首先将所有结果加载到内存中,不与数据库交互,然后使用上一段所述的事务批量更新;或

  • 如果您的数据库更新必然受到慢速网络连接的限制,请使用单独的 inDatabase 调用,这样它就不会在下载时占用 FMDatabaseQueue来自您的网络服务的数据。

最重要的是,通过使用事务或明智地使用单独的 inDatabase 调用,您可以最大限度地减少后台操作占用 FMDatabaseQueue 的时间,并且您可以实现与您的数据库的同步多线程交互,而不会过多地阻塞您的 UI。

关于ios - 在多线程和两个连接上使用 FMDB,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16847194/

相关文章:

java - 循环屏障异常处理

multithreading - 为什么 Pycharm 调试器不会在主线程外的断点处停止?

ios - 避免像在 FMDatabaseQueue 中那样使用带有 block 方法的 __block 变量保留计数

objective-c - FMDB 使用字典查询

javascript - Cordova/Phonegap 全局化与 navigator.language

ios - Xamarin.Forms 方法在某些设备上执行两次

ios - XCTest 应用程序测试和权限警报

objective-c - xCode-如何在发生另一 Action 时停止该 Action

c++ - Qt 工作线程——我必须为每个特定任务继承还是可以使用回调?

iphone - 如何在 FMDatabase 中打开外键?