【问题标题】:Improve speed for updating existing records (~11.000) in Core Data提高更新核心数据中现有记录(~11.000)的速度
【发布时间】:2012-05-11 05:35:21
【问题描述】:

我正在解析最初插入核心数据存储的大量数据。

稍后,我将解析相同的 XML,尽管其中一些可能已更新。然后我要做的是检查具有相同标签的现有记录,如果已经存在,我用数据更新记录。

但是,虽然我的初始解析(大约 11.000 条记录)需要 8 秒左右,但更新似乎很昂贵并且需要 144 秒(这些是模拟器运行,因此在实际设备上要长得多)。

虽然第一次很好(我正在显示一个进度条),但第二次却长得令人无法接受,我想做一些事情来提高速度(即使它发生在单独线程的后台)。

不幸的是,这不是 find-or-create 的问题,因为 XML 中的数据可能已针对个别记录发生了变化,因此每个记录都可能需要更新。

我已经对属性进行了索引,这也加快了初始解析和更新的速度,但它仍然很慢(上面的数字带有索引)。我注意到解析/更新似乎逐渐减慢。虽然最初很快,但随着处理的记录越来越多,它变得越来越慢。

所以最后我的问题是,对于我如何提高更新数据集的速度有什么建议吗?我正在使用 MagicalRecord 来获取记录。代码如下:

Record *record;
if (!isUpdate) {
    record = [NSEntityDescription insertNewObjectForEntityForName:@"Record" inManagedObjectContext:backgroundContext];
} else {
    NSPredicate *recordPredicate = [NSPredicate predicateWithFormat:@"SELF.tag == %@", [[node attributeForName:@"tag"] stringValue]];
    record = [Record findFirstWithPredicate:recordPredicate];
}

【问题讨论】:

  • 您多久在 Context 上调用一次保存?
  • 我总共调用了 18 次。我玩过这个数字,它似乎是整体速度的神奇数字。
  • 如果我没看错的话,当你第一次插入这些记录时,总共只需要大约 8 秒。那么认为如果您删除现有实体然后插入一个包含更新数据的新实体会更快,这是否天真?
  • 这可能会加快速度,但是当数据突然不存在时,我会冒着用户尝试访问数据的风险。
  • 首先,使用“Time Profiler”仪器运行仪器更新。

标签: ios core-data nsfetchrequest


【解决方案1】:

所有性能问题的最初答案是运行仪器。然后,使用这些数据,您可以确定您的问题区域。从那里,您可能对提高性能的某些方面有其他具体的问题。

众所周知,我们人类不擅长识别性能瓶颈。所以,首先使用仪器。它肯定会告诉你你的时间都花在了哪里。

【讨论】:

    【解决方案2】:

    您还可以尝试将 Senior 的答案与属性的散列结合起来。

    在插入散列属性并将该散列存储为Record的一种校验和属性。
    在更新时,您将获取的属性设置为标记和校验和,并对所有项目进行一次获取。然后,当您迭代数据集时,如果校验和与已获取的不同,您可以获取 Record 并更新它。

    【讨论】:

      【解决方案3】:

      与其进行大量的提取,不如对每种实体类型进行一次查询,然后按标签将它们存储在字典中,然后只需检查字典中是否存在具有该键的对象。您应该能够将 propertiesToFetch 设置为仅包含标记,并且应该可以减少开销。

      【讨论】:

      • 这正是我的方法,我不明白为什么 find-or-create 在这里无关紧要,实际上不是 find-and-create 应用的场景吗?
      • 不,不是。如果您需要插入一条不存在的完整记录,则查找--create 很重要。就我而言,如果需要,我需要更新记录。因此,如果 XML 提要有新记录,我都需要插入新记录,但对于数据库中已有的每条记录,我都需要更新它的属性。
      • 让我们仔细检查一下:对于新 xml 中的每个项目,您是否使用“标签”,对核心数据存储中的所有项目执行提取请求以检查是否已经存在这样的一个项目,如果是,更新那个,如果不是,插入?那么,您基本上对新 XML 中的每个项目都进行了获取请求,这难道不是真的吗?如果是这样,你肯定只想做一次提取(获取持久存储中所有现有项目的 NSDictionary (key="Tag", value=Item),并在 NSDictionary 中查找现有项目(并更新它们,批量保存),只有在找不到时才插入新的?
      • 是的,确实如此。第一次运行 = 插入记录,所有后续运行 = 更新或插入所有记录。
      • @senior 我也没有。一个人会做一次提取(NSFetchRequest 项目,只是它是使用 propertiesToFetch 的标签,并使用 key=Tag,value=Item 填充 NSDictionary 与提取的结果。然后,在遍历解析的xml项的同时,检查当前项是否存在于字典中(查找Tag),如果存在,更新对应的项,如果不存在,进行插入,并且(为了安全)将插入的项添加到字典。我看不出这在 runmad 的情况下不起作用的任何原因......
      【解决方案4】:

      您可以尝试的一件事是使用模板 NSPredicate,这样您就不会为正在执行的每个查找/获取重新解析格式字符串。

      所以在你进入你的循环之前:

      NSPredicate *template = [NSPredicate predicateWithFormat:@"SELF.tag == $RECORD_TAG"];
      

      循环内部:

      Record *record;
      if (!isUpdate) {
          record = [NSEntityDescription insertNewObjectForEntityForName:@"Record" inManagedObjectContext:backgroundContext];
      } else {
          NSPredicate *recordPredicate = [];
          record = [Record findFirstWithPredicate:[template predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:[[node attributeForName:@"tag"] stringValue] forKey:@"RECORD_TAG"]];
      }
      

      有关更多信息,请参阅 Apple 的 Predicate Programming Guide

      【讨论】:

      • 帮助了一点。我实际上是为大约 98% 的对象(有多种类型)执行此操作,现在我正在对剩余的对象执行此操作。到目前为止,在 144 秒中为我节省了大约 2 秒 :)
      猜你喜欢
      • 1970-01-01
      • 2012-07-02
      • 1970-01-01
      • 2015-11-26
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多