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

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

FMDatabase用于所有READ查询
所有UPDATE查询的FMDatabaseQueue.

两者都由单例处理,它在应用程序运行时保持两种类型的打开.

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

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

我已经为所有线程使用了一个单一的FMDatabaseQueue,然后执行(读取和更新),但是当使用带有__block变量的读取查询将结果集从回调中取出时相当慢,而另一个线程执行一些插入(在50-100之间)单笔交易).

在它的顶部,数据库通过sqlcipher加密 – 不知道如果它是重要的,但想提到它.所以每次我必须关闭并打开数据库,我正在做一个setKey.

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

UPDATE

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

-(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秒.

或者,如果您的批量更新是必要的缓慢(例如,您使用某种流式协议从Web服务下载某些大数据源),您可以:

>首先将所有结果加载到内存中,没有数据库交互,然后使用上一段所述的交易的批量更新;要么
>如果您的数据库更新必然受到较慢的网络连接的约束,则使用单独的inDatabase调用,以便在从Web服务下载数据时不会调出FMDatabaseQueue.

底线,通过使用事务或明智地使用单独的inDatabase调用,可以最大限度地减少后台操作涉及FMDatabaseQueue的时间,并且可以实现与数据库的同步多线程交互,而不会太大地阻止用户界面.

相关文章

当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple...
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只...
一般在接外包的时候, 通常第三方需要安装你的app进行测...
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应...