Sqlite或Core Data可更新超过50000条记录

时间:2016-09-22 14:22:56

标签: ios sqlite core-data

我目前正在为我的项目使用coredata。但是当api返回应用程序需要更新的54000个对象时,用户必须等待将近2个小时。 这是当前项目的主要问题,我正在考虑使用sqlite而不再使用coredata来更新数千个对象。

使用Sqlite是正确的决定还是对CoreData有任何建议?我无法决定。任何帮助都会很棒。谢谢。

这是我正在做的事情:

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);
        }
}

4 个答案:

答案 0 :(得分:1)

Core Data提供NSBatchUpdateRequest,它允许您直接在持久存储上进行更新,而不涉及在内存中实例化和处理托管对象。

您也应该使用核心数据性能工具运行此代码。如果itemList包含54,000个对象,那么您每次都会对持久性存储执行54,000次提取以检查单个ID。预先获取所有ID然后检查内存中的结果比执行重复获取请求要快得多 - 该原始SQL中的代码几乎与Core Data中的代码一样慢。

此代码也看起来不对:

ItemManagedObject *itemMO;

if(itemMO.count){

如果测试,它永远不会通过,除非你错过了某个地方的线路。

答案 1 :(得分:0)

我从未在sqlite中重做核心数据项目,反之亦然。所以我不能告诉你是否存在性能差异/

然而,54k = 2小时的事情听起来很奇怪。你谈到一个让我怀疑服务器涉及的API,你的问题是关于数据库。当然2个小时听起来太长了,让我想知道你是否对数据库的核心设计有疑问。例如,缺乏索引。根据您的查询和数据库,单个更新可能会触发各种重载处理。

另一个原因是您在设备上处理此列数据的原因。要处理的事情很多,我想知道是否有办法减少音量,有选择地做更新,甚至更好 - 将它移到服务器上。

我认为你需要重新考虑你的问题。提供有关数据库的更多上下文,确切地说您正在使用它以及为什么这样做。

答案 2 :(得分:0)

CoreData不是数据库管理器,而是对象图和持久性管理器。 CoreData可以将其对象存储在sqlite数据库中,也可以存储在XML文件或二进制文件中(开发人员可以选择最适合其需求的选项)。

CoreData和数据库管理器之间的主要区别在于,要使用CoreData访问对象,CoreData需要实例化Objective-C / Swift对应的对象。

Sqlite可以访问部分数据,而无需提取包含数据的完整记录。

然后,CoreData需要维护对象之间的关系图(两个CoreData类之间的关系,一般来说,两种方式)。

因此,在更新54k对象时,您要求CoreData实例化54k对象(在内存中)并最终更新它们的关系。

对于移动设备上的CoreData来说,这是非常繁重的工作。

可能您的CoreData模型未正确优化。 也许您应该定期保存CoreData上下文并刷新CoreData暂存器(包含实际读取或更新对象的内存部分)。

但根据我的经验,CoreData不适合繁重的数据工作。

如果您希望能够从sqlite记录中重新实例化classe对象并管理相当自动的关系,那么使用sqlite重新实现您的需求可能会非常有用,但它是可行的。我在一些项目上做过。这样可以增加一个模型对象,例如Android可以与其他平台共享,因为sqlite可以在很多平台上使用。

还有一件事:sqlite更适合从多个线程使用。 CoreData对此更加敏感,并且需要一个线程上下文,最终需要一些上下文同步。

答案 3 :(得分:0)

2小时很长。这很奇怪。

然而,您可以通过让核心数据减少工作来按摩您的代码。工作少得多。

  1. 执行单次抓取请求而非54K抓取请求
  2. 当属性值未更改时,不要调用托管对象属性setter ,这样就不会有任何对象被不必要地标记为脏,并且Core Data不必执行昂贵但无用的更新调用“save”方法时的对象。
  3. 这将大大减少Core Data执行的工作量以及应用程序的性能。

    第二点很简单,但非常冗长:在调用setter之前将每个单独的属性值与字典值进行比较。

    第一点需要更改算法:

    执行单个获取请求,按id排序(使用[NSFetchRequest setSortDescriptors:])

    按ID排序词典(使用[NSArray sortedArray ...])

    同步两个已排序的列表(两个列表的排序至关重要):

    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不会将慢速算法变成快速算法