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的时间,并且可以实现与数据库的同步多线程交互,而不会太大地阻止用户界面.