【问题标题】:fetch request performance issues获取请求性能问题
【发布时间】:2010-12-21 09:57:36
【问题描述】:

在使用大约 100 次查询 sqlite 数据库时,我遇到了一些严重的性能问题。 25k 行。 我想要做的是以下内容:当用户在文本字段中输入内容时,我想在作为键盘的 inputAccessoryView 的表格视图中给他自动完成建议。每次他输入一个新角色时,都会启动 4 个新查询以搜索适当的建议。我使用 GCD 和块在单独的线程中执行此操作。 但是性能太低了。这是一个查询的代码:

- (void) queryDatabase:(NSString *) searchString
{   
[self.fetchedResults removeAllObjects];

dispatch_queue_t fetchQueue = dispatch_queue_create("Fetch Queue", NULL);

dispatch_async(fetchQueue,^{
    NSError *error = nil;
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    context.undoManager = nil;
    [context setPersistentStoreCoordinator: self.persistentStoreCoordinator];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"reducedTownName like %@", [searchString stringByAppendingString:@"*"]];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = [NSEntityDescription entityForName:@"Station" inManagedObjectContext:context];
    request.fetchLimit = 20;
    [request setIncludesPropertyValues:NO];
    request.predicate = predicate;
    request.resultType = NSManagedObjectIDResultType;

    NSArray *results = [context executeFetchRequest:request error:&error];
    NSEnumerator *e = [results objectEnumerator];
    NSManagedObjectID *objectId = nil;

    while (objectId = [e nextObject]) 
    {
        Station *station = (Station *) [self.managedObjectContext objectWithID:objectId];
        if ( ![self.fetchedResults containsObject:station])
            [self.fetchedResults addObject:station];
    }       

    dispatch_async(dispatch_get_main_queue(),^{
        [self.tableView reloadData];
    });
    [request release];
    [context release];
});
    //do 3 more queries similar to the first (only predicate changes)
    dispatch_release(fetchQueue);
}

我使用 NSArray (fetchedResults) 来保存返回的 Entities 并使用该 Array 中的数据更新 tableView。

有人在这段代码中看到了任何性能杀手,或者对我有其他建议吗?

【问题讨论】:

  • 性能如何(每个查询以毫秒为单位)?
  • 我用 BEGINSWITH 替换了 LIKE,现在查询时间范围从即时到大约 400 毫秒,这在很大程度上是可以接受的。无论如何,我愿意接受进一步提高性能的建议。

标签: iphone performance sqlite core-data


【解决方案1】:

我想我可能已经发现了。

您进行查询以获取实体并将其放入结果数组中。那是第 1 个查询,它已经过优化(假设没有连接等,但看起来没有)

然后,您检查所有结果以删除结果中已经存在的重复项。这意味着您执行一个新查询来获取每个 Station NSManagedObject。如果您想避免重复,则必须在不调用 objectWithID 的情况下执行此操作,因为这会从 CoreData 获取对象。

最坏的情况,要获得 20 个站点结果,您需要 21 个查询。不好。

由于 sqlite3 是单线程的,为什么不只创建一个大谓词而不是 4 个较小的谓词 - 然后您可以删除谓词中的重复项,并且在绘制它之前不需要遍历数组站的表格视图单元格。

我以前在查找时遇到过这个问题,必须通过在数据库中创建一个单独的表来解决它。在您的情况下,此表(称为 station_search)将仅包含电台名称(当然会被索引)和 stationId。然后我在这张表上进行搜索(这会很快,因为它没有大量数据可供搜索)。

得到结果后,我只需使用 stationId 从主站表中获取 Station仅在需要时,即我在表格中绘制单元格。

我还可以使用NSFetchedResultsController 将我的结果一起批处理。

这变得有点乱 - 如果您有任何问题,尽管问!

希望对你有帮助,

山姆

【讨论】:

  • 嗯我只在辅助线程中获取对象ID,并将其传递给主托管对象上下文,以合并结果。这是 Apple 描述的一种方式。还是我误会了你?无论如何,我用 BEGINSWITH 替换了 LIKE,性能大大提高。一旦我包含排序描述符,性能就会下降,可能是因为它会获取所有记录,对它们进行排序并只返回 20 个。有没有办法以这种方式构建数据库,即请求将自动按字典顺序返回前 N 个结果而不使用排序描述符?
  • 你呢?如果你在Station *station = (Station *) [self.managedObjectContext objectWithID:objectId]; 行设置断点,它说它在哪个线程上运行?
  • 它说 Thread-3-。这绝对不是主线。
  • 是的,你是对的,如果你传递 ids 这绝对是正确的方法:)
【解决方案2】:

好吧,我终于明白了。将 LIKE 查询替换为

reducedTownName >= %@ AND reducedTownName <= %@

第一个参数是搜索词,第二个参数也是搜索词,除了最后一个字母被后面的替换,例如。

reducedTownName >= 'oak' AND reducedTownName <= 'oal'

这是非常快的,给了我想要的结果。我还可以在主线程中移动查询,这给了我额外的性能提升。

问候

山姆

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-26
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 2013-02-28
    相关资源
    最近更新 更多