ach*_*sen 12 multithreading fmdb ios
我在我的应用程序中使用两种不同类型的fmdb连接:
所有READ查询的FMDatabase和所有UPDATE查询的FMDatabaseQueue.
两者都由一个单例处理,这使得两种类型在应用程序运行时保持打开状态.
读取和更新查询都在不同的线程中使用,因为我的应用程序中的某些任务是在后台进行的; 比如从服务器获取数据并通过FMDatabaseQueue在自己的后台线程中将其插入数据库 - 同时通过FMDatabase从db读取一些信息并在主线程上用它更新ViewController.
我的问题是,通过FMDatabaseQueue将数据插入数据库后,第二个连接(FMDatabase)不会返回更新的信息,因为它找不到它们.但我知道插入的数据是因为我使用db浏览器工具检查了数据库+插入时没有发生错误.为避免这种情况,我必须关闭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];
}
}];
}
Run Code Online (Sandbox Code Playgroud)
和一个读取查询
-(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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
正如我所说,使用此代码时,FMDatabase连接不会获取通过FMDatabaseQueue插入的信息.
就个人而言,我建议FMDatabaseQueue对两个线程使用单个,并让队列协调两个线程上的操作.这就是它的创造.它完全消除了那些"数据库繁忙"的问题.
在性能更新中,如果进行批量更新,您是在更新之前和结束时使用该FMDatabase方法吗?或者使用该方法.在我的测试中插入没有事务的10,000条记录需要36.8秒,但是事务需要0.25秒.beginTransactioncommitinTransaction
或者,如果您的批量更新速度很慢(例如,您使用某种流协议从Web服务下载某些大数据源),您可以:
首先将所有结果加载到内存中,不进行数据库交互,然后将批量更新与事务一起使用,如上一段所述; 要么
如果您的数据库更新必然受到慢速网络连接的限制,那么请使用单独的inDatabase调用,以便FMDatabaseQueue在从Web服务下载数据时不会占用.
最重要的是,通过使用事务或明智地使用单独的inDatabase调用,您可以最大限度地缩短后台操作所需的时间,FMDatabaseQueue并且您可以实现与数据库的同步多线程交互,而不会过多地阻止UI.
| 归档时间: |
|
| 查看次数: |
8123 次 |
| 最近记录: |