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; }
解决方法
FMDatabaseQueue
,并让队列协调两个线程上的动作.这就是为它创造的.它完全消除了这些“数据库忙”问题.
在性能更新中,如果进行批量更新,则在更新之前是否使用FMDatabase方法beginTransaction
,最后使用commit
?或使用inTransaction方法.在我的测试中插入10,000条记录没有交易需要36.8秒,但是事务需要0.25秒.
或者,如果您的批量更新是必要的缓慢(例如,您使用某种流式协议从Web服务下载某些大数据源),您可以:
>首先将所有结果加载到内存中,没有数据库交互,然后使用上一段所述的交易的批量更新;要么
>如果您的数据库更新必然受到较慢的网络连接的约束,则使用单独的inDatabase调用,以便在从Web服务下载数据时不会调出FMDatabaseQueue.
底线,通过使用事务或明智地使用单独的inDatabase调用,可以最大限度地减少后台操作涉及FMDatabaseQueue的时间,并且可以实现与数据库的同步多线程交互,而不会太大地阻止用户界面.