核心数据获取非常慢

Posted

技术标签:

【中文标题】核心数据获取非常慢【英文标题】:Core Data fetch is extremely slow 【发布时间】:2012-06-23 00:47:54 【问题描述】:

我正在运行一个非常基本的 NSFetchRequest 来获取实体 MessageObject。我只有 2000 个消息对象,我想全部检索它们。但是,由于某些奇怪的原因,获取请求需要 10 多秒!

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MessageObject" inManagedObjectContext:appDelegate.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort= [[NSSortDescriptor alloc] initWithKey:@"createDate" ascending:NO selector:@selector(compare:)];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:5];

就是这样,这就是我的获取请求。我什至没有使用谓词,它需要超过 10 秒。我对可能导致这种情况的原因一无所知。如果有人有任何想法或起点,请分享。

我也尝试过启用 SQLite 调试日志记录 (-com.apple.CoreData.SQLDebug 1),但我只是从这个简单的提取中获得了数千行输出。这正常吗?

 2012-06-22 19:39:59.171 myapp[81825:15e03] about to execute fetch
 2012-06-22 19:39:59.172 myapp[81825:15e03] CoreData: sql: SELECT 0, t0.Z_PK FROM ZMBNOTEOBJECT t0 ORDER BY t0.ZCREATEDATE DESC
 2012-06-22 19:39:59.178 myapp[81825:15e03] CoreData: annotation: sql connection fetch time: 0.0061s
 2012-06-22 19:39:59.179 myapp[81825:15e03] CoreData: annotation: total fetch execution time: 0.0067s for 2052 rows.
 2012-06-22 19:39:59.179 myapp[81825:15e03] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZAUTHOREMAIL, t0.ZAUTHORNAME, t0.ZCREATEDATE, t0.ZISGLOBAL, t0.ZISLOCKED, t0.ZISNEW, t0.ZISPENDINGDELETE, t0.ZISPENDINGLIKE, t0.ZISPENDINGREAD, t0.ZISPENDINGSYNC, t0.ZLASTUPDATED, t0.ZLOCALLYMODIFIEDDATE, t0.ZMAINIDEA, t0.ZMETALASTUPDATED, t0.ZNOTEID, t0.ZNUMBEROFCHILDREN, t0.ZPARENTAUTHOREMAIL, t0.ZPARENTNOTEID, t0.ZROOTAUTHOREMAIL, t0.ZROOTNOTEID, t0.Z4PENDINGADDNOTES, t0.Z4PENDINGREMOVENOTES FROM ZMBNOTEOBJECT t0 WHERE  t0.Z_PK IN  (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)  ORDER BY t0.ZCREATEDATE DESC LIMIT 15
 2012-06-22 19:39:59.180 myapp[81825:15e03] CoreData: annotation: sql connection fetch time: 0.0008s
 2012-06-22 19:39:59.181 myapp[81825:15e03] CoreData: annotation: total fetch execution time: 0.0018s for 15 rows.
 2012-06-22 19:39:59.182 myapp[81825:15e03] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZAUTHOREMAIL, t0.ZAUTHORNAME, t0.ZCREATEDATE, t0.ZISGLOBAL, t0.ZISLOCKED, t0.ZISNEW, t0.ZISPENDINGDELETE, t0.ZISPENDINGLIKE, t0.ZISPENDINGREAD, t0.ZISPENDINGSYNC, t0.ZLASTUPDATED, t0.ZLOCALLYMODIFIEDDATE, t0.ZMAINIDEA, t0.ZMETALASTUPDATED, t0.ZNOTEID, t0.ZNUMBEROFCHILDREN, t0.ZPARENTAUTHOREMAIL, t0.ZPARENTNOTEID, t0.ZROOTAUTHOREMAIL, t0.ZROOTNOTEID, t0.Z4PENDINGADDNOTES, t0.Z4PENDINGREMOVENOTES FROM ZMBNOTEOBJECT t0 WHERE  t0.Z_PK IN  (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)  ORDER BY t0.ZCREATEDATE DESC LIMIT 15
 2012-06-22 19:39:59.186 myapp[81825:15e03] CoreData: annotation: sql connection fetch time: 0.0042s
 2012-06-22 19:39:59.187 myapp[81825:15e03] CoreData: annotation: total fetch execution time: 0.0049s for 15 rows.
 2012-06-22 19:39:59.187 myapp[81825:15e03] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZAUTHOREMAIL, t0.ZAUTHORNAME, t0.ZCREATEDATE, t0.ZISGLOBAL, t0.ZISLOCKED, t0.ZISNEW, t0.ZISPENDINGDELETE, t0.ZISPENDINGLIKE, t0.ZISPENDINGREAD, t0.ZISPENDINGSYNC, t0.ZLASTUPDATED, t0.ZLOCALLYMODIFIEDDATE, t0.ZMAINIDEA, t0.ZMETALASTUPDATED, t0.ZNOTEID, t0.ZNUMBEROFCHILDREN, t0.ZPARENTAUTHOREMAIL, t0.ZPARENTNOTEID, t0.ZROOTAUTHOREMAIL, t0.ZROOTNOTEID, t0.Z4PENDINGADDNOTES, t0.Z4PENDINGREMOVENOTES FROM ZMBNOTEOBJECT t0 WHERE  t0.Z_PK IN  (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)  ORDER BY t0.ZCREATEDATE DESC LIMIT 15
 2012-06-22 19:39:59.188 myapp[81825:15e03] CoreData: annotation: sql connection fetch time: 0.0008s
 2012-06-22 19:39:59.189 myapp[81825:15e03] CoreData: annotation: total fetch execution time: 0.0014s for 15 rows.
... (thousands of more lines similar to above)

我对阅读这篇文章不是很熟悉,但它似乎在 0.0067 秒内获取了 2052 行。那么为什么在那之后它继续做更多的事情呢?如果完成获取行,请求不应该完成吗?是数据有问题还是怎么的?

此外,我还删除了 setFetchBatchSize - 这消除了数千行,但获取请求仍然需要很长时间。这是我得到的输出:

2012-06-22 20:07:25.316 myapp[8927:707] about to execute fetch
 2012-06-22 20:07:25.322 myapp[8927:707] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZAUTHOREMAIL, t0.ZAUTHORNAME, t0.ZCREATEDATE,t0.ZISLOCKED, t0.ZISNEW, t0.ZISPENDINGDELETE, t0.ZISPENDINGSYNC, t0.ZLASTUPDATED, t0.ZLOCALLYMODIFIEDDATE, t0.ZMAINIDEA, t0.ZMETALASTUPDATED, t0.ZNOTEID, t0.ZNUMBEROFCHILDREN, t0.ZPARENTAUTHOREMAIL, t0.ZPARENTNOTEID, t0.ZROOTAUTHOREMAIL, t0.ZROOTNOTEID, t0.Z4PENDINGADDNOTES, t0.Z4PENDINGREMOVENOTES FROM ZMBNOTEOBJECT t0 ORDER BY t0.ZCREATEDATE DESC
 2012-06-22 20:07:26.758 myapp[8927:707] CoreData: annotation: sql connection fetch time: 1.0891s
 2012-06-22 20:07:26.763 myapp[8927:707] CoreData: annotation: total fetch execution time: 1.4407s for 4000 rows.
 2012-06-22 20:07:35.967 myapp[8927:707] finished fetching

奇怪的是,在 20:07:26.763,它显然说 4000 行需要 1.4407 秒,但不是再过 9 秒,我才得到输出说“完成获取”(这是一个出现在 @ 之后的 NSLog 语句987654325@) 那是怎么回事?

【问题讨论】:

【参考方案1】:

移除 setFetchBatchSize。

如果您的意图是一次加载所有内容,只需将其删除即可。

另外,如果你需要加载所有属性,添加:

fetchRequest.returnsObjectsAsFaults = NO;

它将加载每个实体并填充属性。

您可能只想加载某些属性,因此请使用它来选择您需要的内容:

fetchRequest.propertiesToFetch = ...

你已经完成了。

【讨论】:

好吧,我真的不想获取所有内容,我这样做是为了测试。实际上,我会有一个谓词,但只是为了测试,我想看看在没有谓词的情况下获取所有实体需要多长时间(因为有谓词也需要太长时间)。但是去掉 setFetchBatchSize 之后,我就再也没有得到几千行的 SQL 日志了。但我确实想保留 batchSize。 如果你保留它,那么 Core Data 将不得不解决数千个错误,因此你会看到请求。这需要大量时间。 但它不应该只在需要时解决故障吗?我的表格一次只显示 4 个单元格,那么为什么它会立即在数千个对象中出错? 使用 Instruments 分析您的应用程序并准确找出谁在做什么。你会更好地理解吃那么多时间是什么。如果没有关于您的设置和架构的更多详细信息,很难猜测会发生什么。

以上是关于核心数据获取非常慢的主要内容,如果未能解决你的问题,请参考以下文章

cocos2d:SQLite 还是核心数据? - 需要重新设计数据库,因为我当前的应用程序非常慢或有时在没有 WiFi 的情况下崩溃

核心数据对象等级(慢)

尝试使用 Sqoop 从 DB2 获取数据到 Hbase 非常慢

从核心数据到表的数据加载非常缓慢

尝试将服务器内容同步到 Core Data 时出现问题 - Magical Record 插入过程非常慢

lucene3.0 通过IndexSearcher.doc(i)获取Document 非常慢,如何提高性能?