Sqlite或核心数据来更新超过50000条logging
我正在为我的项目使用coredata。 但是,当API返回应用程序需要更新的54000个对象时,用户需要等待将近2个小时。 这是当前项目的主要问题,我正在考虑使用sqlite而不再使用coredata来更新数千个对象。
使用Sqlite是正确的决定还是有CoreData的build议? 我不能决定。 任何帮助将是伟大的。 谢谢。
这是我在做什么:
NSManagedObjectContext *privateObjectContext = [AppDelegate appDelegate].privateManagedObjectContext; [privateObjectContext performBlock:^{ int i = 1; for (NSDictionary *item in itemlist) { i++; [fetchRequest setPredicate:[NSPredicate predicateWithFormat: @"itemID == %@",[item objectForKey:@"item_id"] ]]; NSError *error; NSMutableArray *inventories = [[NSMutableArray alloc]initWithArray: [privateObjectContext executeFetchRequest:fetchRequest error:&error]]; ItemManagedObject *itemMO; if(inventories.count){ itemMO = inventories.firstObject; }else{ itemMO = [NSEntityDescription insertNewObjectForEntityForName:@"ItemObject" inManagedObjectContext:privateObjectContext]; } [itemMO prepareWithDictionary:item]; } NSError *error; if (![privateObjectContext save:&error]) { completionHandler(NO); } }
2个小时很长。 这很奇怪。
然而,你可以通过让核心数据做更less的工作来按摩你的代码。 更less的工作。
- 执行单个提取请求而不是54K提取请求
- 当一个属性值没有改变时 , 不要调用一个托pipe对象属性设置器 ,这样就不会有不必要的标记为脏的对象,并且当“save”方法是“核心数据”时,核心数据不必执行代价高昂但无用的更新调用。
这将显着减lessCore Data所执行的工作量,以及您的应用程序的性能。
第二点很简单,但非常冗长:在调用setter之前,比较每个单独的属性值和字典值。
第一点需要改变algorithm:
执行单个提取请求,按idsorting([NSFetchRequest setSortDescriptors:])
按idsorting字典([NSArray sortedArray …])
同步两个sorting的列表(两个列表sorting是最重要的):
NSEnumerator *itemMOEnum = [itemMOs objectEnumerator]; NSEnumerator *dicEnum = [dictionaries objectEnumerator]; ItemManagedObject *itemMO = [itemMOEnum nextObject]; NSDictionary *itemDic = [dicEnum nextObject]; while (itemDic) { NSComparisonResult comparison = itemMO ? [itemDic[@"item_id"] compare:itemMO.itemID] : NSOrderedAscending; switch (comparison) { case NSOrderedSame: // id present in both lists: update [itemMO prepareWithDictionary:itemDic]; itemMO = [itemMOEnum nextObject]; itemDic = [dicEnum nextObject]; break; case NSOrderedAscending: { // id present only in dictionaries: create itemMO = [NSEntityDescription insertNewObjectForEntityForName:@"ItemObject" inManagedObjectContext:privateObjectContext]; [itemMO prepareWithDictionary:itemDic]; itemDic = [dicEnum nextObject]; } break; case NSOrderedDescending: // id present only in managed object: delete or do nothing itemMO = [itemMOEnum nextObject]; break; } } while (itemMO) { // id present only in managed object: delete or do nothing itemMO = [itemMOEnum nextObject]; }
并保存。
最后,也许SQLite会更快(请参阅https://github.com/groue/GRDB.swift/wiki/Performance,以便比较Core Data与SQLite库的性能)。
但是SQLite不会把慢速algorithm变成一个快速的algorithm 。
核心数据提供NSBatchUpdateRequest
,它允许您直接在持久性存储上进行更新,而不涉及在内存中实例化和处理pipe理对象。
您应该使用核心数据性能工具来运行此代码。 如果itemList
包含54,000个对象,那么您将对持久性存储执行54,000次提取,以便每次检查一个ID。 要提前获取所有ID,然后检查内存中的结果要比执行重复的提取请求要快得多 – 原始SQL中的代码几乎与核心数据中的代码一样慢。
此代码也看起来不正确:
ItemManagedObject *itemMO; if(itemMO.count){
如果testing,除非你错过了某一行,否则永远不会通过。
我从来没有在sqlite中重做核心数据项目,反之亦然。 所以我不能告诉你是否有性能差异/
但54k = 2小时的事情听起来很奇怪。 你谈论一个API,这让我怀疑涉及服务器,你的问题是关于数据库。 当然,2小时听起来太长了,让我怀疑你的数据库的核心devise是否有问题。 例如缺乏索引。 根据您的查询和数据库,单个更新可能会触发各种重型处理。
另一个是你为什么要在设备上处理这一列的数据。 这是很多要处理,我不知道是否有办法减less音量下降,有select地做更新甚至更好 – 将其移动到服务器。
我想你需要重新思考你的问题。 提供关于数据库的更多上下文,正是你在做什么以及为什么。
CoreData不是一个数据库pipe理器,而是一个对象图和持久性pipe理器。 CoreData可以将其对象存储在sqlite数据库中,也可以存储在XML文件或二进制文件中(开发人员可以select最适合其需求的选项)。
CoreData和数据库pipe理器的主要区别在于,要访问CoreData对象,CoreData需要实例化Objective-C / Swift对应的对象。
Sqlite可以访问部分数据,而不必提取包含数据的完整logging。
然后,CoreData需要维护对象之间的关系图(2个CoreData类之间的关系,并且一般来说都是这两种关系)。
因此,当更新54k对象时,您要求CoreData实例化54k对象(在内存中)并最终更新它们的关系。
对于移动CoreData来说这是非常繁重的工作。
也许你的CoreData模型没有被正确的优化。 也许你应该定期保存CoreData上下文,并刷新CoreData暂存器(包含实际读取或更新对象的内存部分)。
但是根据我的经验,CoreData不适合繁重的数据工作。
用sqlite重新实现你的需求可能是一些工作,如果你想能够从sqlitelogging重新实例化你的类对象,并pipe理相当自动的关系,但它是可行的。 我做了一些项目。 这增加了一个模型对象的好处,因为sqlite在很多平台上都可用,所以比如像Android这样的其他平台可以共享更多的模型对象。
还有一件事:sqlite更适合从多个线程使用。 CoreData对此更加敏感,并且需要一个线程的上下文,最终还有一些上下文同步。