【问题标题】:Efficient way to perform bulk INSERT/UPDATE/DELETE in CoreData.在 CoreData 中执行批量 INSERT/UPDATE/DELETE 的有效方法。
【发布时间】:2013-12-13 12:18:18
【问题描述】:

我有一个包含 200,000 个项目的 JSON 对象。我需要遍历这些对象,并确定它们是否存在并执行相关操作(插入/更新/删除)。其外壳如下所示。当然,它实际上还没有保存任何东西。更多的是看看这条路需要多长时间。在 iPhone 4 上执行此操作大约需要 8 分钟,考虑到甚至还没有发生任何变化,这似乎很疯狂。

有没有更有效的方法来处理这个问题?

任何建议或指示将不胜感激。

- (void) progressiveInsert
{
    prodAdd = 0;
    prodUpdate = 0;
    prodDelete = 0;

    dispatch_queue_t backgroundDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_async(backgroundDispatchQueue,
                   ^{
                       _productDBCount = 0;

                       NSLog(@"Background Queue");
                       NSLog(@"Number of products in jsonArray: %lu", (unsigned long)[_products count]);

                       NSManagedObjectContext *backgroundThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
                       [backgroundThreadContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
                       [backgroundThreadContext setUndoManager:nil];

                       [fetchRequest setPredicate:predicate];
                       [fetchRequest setEntity:[NSEntityDescription entityForName:@"Products" inManagedObjectContext:_managedObjectContext]];
                       [fetchRequest setIncludesSubentities:NO]; //Omit subentities. Default is YES (i.e. include subentities)
                       [fetchRequest setFetchLimit:1];

                       [_products enumerateObjectsUsingBlock:^(id product, NSUInteger idx, BOOL *stop) {

                           predicate = [NSPredicate predicateWithFormat:@"code == %@", [product valueForKey:@"product_code"]];
                           [fetchRequest setPredicate:predicate];

                           NSError *err;
                           NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&err];

                           if (fetchedObjects == nil) {

                               if ([[product valueForKey:@"delete"] isEqualToNumber:[NSNumber numberWithBool:TRUE]]){
                                   prodDelete += 1;
                               } else {
                                   prodAdd += 1;
                               }

                           } else {

                               if ([[product valueForKey:@"delete"] isEqualToNumber:[NSNumber numberWithBool:TRUE]]){
                                   prodDelete += 1;
                               } else {
                                   prodUpdate += 1;
                               }

                           }

                           dispatch_sync(dispatch_get_main_queue(), ^
                                         {

                                             self.productDBCount += 1;
                                             float progress = ((float)self.productDBCount / (float)self.totalCount);
                                             _downloadProgress.progress = progress;

                                             if (_productDBCount == _totalCount){
                                                 NSLog(@"Finished processing");
                                                 _endProcessing = [NSDate date];
                                                 [_btn.titleLabel setText:@"Finish"];
                                                 NSLog(@"Processing time: %f", [_endProcessing timeIntervalSinceDate:_startProcessing]);
                                                 NSLog(@"Update: %i // Add: %i // Delete: %i", prodUpdate, prodAdd, prodDelete);
                                                 [self completeUpdateProcess];

                                             }

                                         });


                       }];


                   });
}

【问题讨论】:

标签: ios iphone core-data ios6


【解决方案1】:

看看 《核心数据编程指南》中的Implementing Find-or-Create Efficiently

(更新:当前的核心数据编程指南中不再存在这一章。存档版本可以在 http://web.archive.org/web/20150908024050/https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html.)

其中一个关键思想不是对每个产品执行一个获取请求,而是执行一个 带有谓词的“批量获取”

[NSPredicate predicateWithFormat:@"code IN %@", productCodes]

其中productCodes 是来自您的 JSON 数据的“许多”产品代码的数组。 当然,您必须找到最佳的“批量大小”。

【讨论】:

  • 啊,好的。这就说得通了。这是我应该在“_products”的现有迭代中做的事情,还是以不同的方式?例如,将产品代码添加到数组中。然后,当数组计数达到 X (100) 时,它执行批量获取,随后执行 CoreData 操作(插入/更新/删除)。
  • @LukeSmith:我认为没有标准的方法可以做到这一点。您所描述的是一种可能的解决方案。
  • @Martin R,链接已损坏
  • @Bearwithme:核心数据编程指南现在在这里:developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…。它已被大幅改写(参见“文档修订历史”)。不幸的是,各个部分已被删除,而“高效实现查找或创建”似乎就是其中之一。
【解决方案2】:

有了这么多对象,我认为您需要开始非常聪明地处理您的数据和系统,并在获取 200K JSON 对象之前寻找其他方法来修剪您的项目。您说您使用 Core Data 并且在 iPhone 上,但您没有指定这是否是客户端/服务器应用程序(从手机访问 Web 服务器)。我会尽量让我的建议保持一般性。

确实,您应该在当前 JSON 之外思考,更多地考虑其他数据/元数据,这些数据/元数据可以提供有关您在合并/更新之前真正需要获取的内容的提示。听起来您正在同步两个数据库(电话和远程)并使用 JSON 作为传输方式。

  1. 你能给你的数据加上时间戳吗?如果您知道上次更新手机数据库的时间,则只需拉取在那次之后更改的数据。
  2. 您可以按部分/分区发送数据吗? 1000-10000 的分组可能更易于管理。
  3. 您能否将数据划分为与用户/应用程序或多或少相关的部分?这样,用户最先触摸的项目会先更新。
  4. 如果您的数据是地理数据,您可以先将数据发送到感兴趣的区域附近吗?
  5. 如果您的数据是产品,您能否先发送用户最近查看过的数据?
  6. 如果您的数据是分层的,您能否将根节点标记为已更改(或再次标记时间戳)并仅更新已更改的子树?

在任何系统中,无论是联网的还是本地数据库,我都会犹豫是否尝试合并来自 200K 项目列表的更新,除非它是一个非常简单的列表(如数字合并排序)。这是对时间和网络资源的巨大浪费,而且不会让您的客户非常满意。

【讨论】:

  • 目的是将这些记录的大部分包含在预填充的数据库中。但是,由于这些记录可能需要以大约 1000 条/天的速度进行更新,因此如果有人没有每周更新一次以上,则需要很长时间。理想情况下,没有人应该一次更新这么多项目,这只是一种预防措施。使用收到的最后一次更新的 ID 请求后续更新(与您的时间戳建议的目的相同),以便只收到相关的更新。
  • 我有两个不同的更新路径。一种用于 CD 实体中没有记录的情况,它只是批量导入“当前”数据(不是更新,数据集的最新完整版本)。这在 iPhone 4 上大约需要 4 分钟。另一个是当有现有记录时,它需要进行 INSERT/UPDATE/DELETE 迭代(如本文所述)。
  • 卢克,即使您找到一种方法可以将 CD 更改速度提高 10 倍(我相信您会喜欢较小的更新),您仍然需要 30-60 秒才能获得 200K。即使每天 1000 次,也就是 7000 次/周或 10 秒,没有加速。我一直想知道你的系统设计。你的用例是什么?当有可用网络或大部分断开连接并且用户触摸按钮以更新数据时,是否经常使用该应用程序?是自动的吗?您真的需要在手机上立即更新每个值吗? Google 可以在网络上自动完成搜索字词,您迫切需要让所有内容立即呈现吗?
  • 我不能给出任何细节。本质上,它是一个产品信息目录,通常在没有可用网络连接时使用。我们已经考虑过在需要时进行实时查找的想法,但目前它根本不可行。更新将由用户触发,很可能通过某种形式的可配置通知频率来确保定期完成。该应用在没有数据的情况下基本上是无用的,如果某些信息丢失,也会引发法律问题。
  • 好的,我想我们已经无法为您提供帮助了 - 抱歉。我已经完成了一些关于 CD 的工作,但是您遇到了一个具有挑战性和限制性的问题,并且将不得不求助于螺栓拧紧来尽可能多地从中挤出性能。您可能会考虑从 CD 切换到 alt。数据库以获得更多性能。这有其自身的成本。这里有两个很棒的blogposts,它们讨论了为什么应该使用 CD,以及何时可能适合转换。
【解决方案3】:

不要处理单个项目,而是批量处理。目前,您向上下文发出大量获取请求,这些请求需要时间(使用 Core Data Instruments 工具查看)。如果您最初将处理的批量大小设置为 100,则获取该组 id,然后在本地检查获取结果数组中是否存在。

【讨论】:

    猜你喜欢
    • 2012-11-02
    • 2012-05-12
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    • 2013-09-27
    • 1970-01-01
    相关资源
    最近更新 更多