SQL 与核心数据的真实世界性能:需要透视和可能的特定修复

Posted

技术标签:

【中文标题】SQL 与核心数据的真实世界性能:需要透视和可能的特定修复【英文标题】:Real World Peformance of SQL vs Core Data: Need Perspective and Perhaps Specific Fixes 【发布时间】:2012-10-27 16:40:58 【问题描述】:

我有一个文字游戏应用程序,它可以访问单词和线索列表,大约有 10 万个条目。数据库只被访问,从不改变。我使用 SQL 方法构建了应用程序,它在 ios 6 上运行良好,但在 iOS 5 上从数据库中获取新线索的时间非常慢:

iOS 5,使用 SQL 从 100K 中获取一条记录,大约需要 12 秒。 iOS 6,使用 SQL 从 100K 中获取一条记录,需要 大约 700-1000 毫秒。

这两个都在 32 GB iPod Touch 上。

鉴于这种性能,我使用 Core Data 制作了一个版本。我的方法通过首先计算适合查询的记录,然后随机选择一个来获得随机数据库条目。代码如下。我读到的所有内容都表明 Core Data 会更快:

iOS 5,计数记录大约需要 4 秒,并检索其中之一 这些记录大约需要 50 - 1500 毫秒。总时间约为 5 秒。 iOS 6,计数记录需要 2 秒多一点,检索 其中一项记录大约需要 300-500 毫秒。总共大约 3 秒。

因此,与 SQL 相比,Core Data 在 iOS 5 上更快,但在 iOS 6 上更慢。就我而言,无论哪种方式,性能都太慢了。我知道开销来自下面给出的方法(对于核心数据版本)。所以,两个问题:

    有关此问题的任何一般性建议,以了解它并提高性能? 具体来说,下面附加的核心数据代码呢:我做了什么愚蠢的事情来减慢它的速度吗?还是我应该添加其他东西来加快速度?这是我第一次尝试 Core Data。

谢谢。

- (NSArray *) randomClue 

NSManagedObjectContext* context = [self managedObjectContext];

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"A"];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"WL28"
                               inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSPredicate *predicate = [self createSearchQuery];
[fetchRequest setPredicate:predicate];

NSError *error;

NSDate *date1 = [NSDate date];
NSString *timeString1 = [formatter stringFromDate:date1];

int resCount = [context countForFetchRequest:fetchRequest
                                       error:&error];

NSDate *date2 = [NSDate date];
NSString *timeString2 = [formatter stringFromDate:date2];

int t1 = [timeString1 intValue];
int t2 = [timeString2 intValue];
int d1 = t2-t1;    
NSLog(@"randomClue:");
NSLog(@"    Time to count array entries: %i", d1);

int ranNum = arc4random_uniform(resCount-1);
int ranNum2 = ranNum + 1;

// Now we fetch just one answer object, not a whole database or even a piece of it!

[fetchRequest setReturnsObjectsAsFaults:YES];
[fetchRequest setPropertiesToFetch:nil];
[fetchRequest setFetchLimit:1];
[fetchRequest setFetchOffset:ranNum2];

NSDate *date3 = [NSDate date];
NSString *timeString3 = [formatter stringFromDate:date3];

self.wl28 = [context executeFetchRequest:fetchRequest error:&error];

NSDate *date4 = [NSDate date];
NSString *timeString4 = [formatter stringFromDate:date4];

int t3 = [timeString3 intValue];
int t4 = [timeString4 intValue];
int d2 = t4-t3;
NSLog(@"    Time to retrieve one entry: %i", d2);

return self.wl28;

编辑:在下方添加 createSearchQuery

- (NSPredicate *)createSearchQuery 

NSMutableArray *pD = [[GameData gameData].curData valueForKey:@"persData"];
NSNumber *currMin = [pD objectAtIndex:0];
NSNumber *currMax = [pD objectAtIndex:1];
NSNumber *dicNo = [pD objectAtIndex:2];
NSString *dict = nil;

if ([dicNo intValue] == 0) dict = @"TWL";
if ([dicNo intValue] == 1) dict = @"LWL";

NSPredicate *dictPred = [NSPredicate predicateWithFormat:@"dict == %@", dict];
NSPredicate *lowNoCPred = [NSPredicate predicateWithFormat:@"noC >= %@", currMin];
NSPredicate *highNoCPred = [NSPredicate predicateWithFormat:@"noC <= %@", currMax];

NSPredicate *query = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray
                      arrayWithObjects:dictPred, lowNoCPred, highNoCPred,nil]];
NSLog(@"%@", query);
return query;

【问题讨论】:

如果数据库是只读的,为什么每次都统计条目? 根据用户设置存在子集,这反过来会影响查询。最大的子集约为 70K,这就是性能数据的基础。这些数量不是无限的,所以我可能会预先计算这些以节省一些时间。 如果你告诉我们createSearchQuery方法会更容易。 @TomaszZabłocki 感谢您查看此内容:我将代码添加到原始帖子中。 【参考方案1】:

您可以尝试将索引添加到实体内的 noC 和/或 dict 属性。它可能会加快您的查询时间。

【讨论】:

你能指点我一些文档吗?我已经看到过索引核心数据结构的提及,但是在NSPredicate 或核心数据编程指南的文档中都没有出现索引这个词。 OK 在 Xcode 中打开 xcdatamodel,切换到网格编辑器样式,然后选择您的实体,然后属性 noC 或 dict。在右侧选择它后,您将拥有 Data Model Inspector。有属性索引所以只需检查它。 Wikipedia - about index 很棒的电话!这将在 iOS 6 上检索单个条目的时间减少到大约 50-200 毫秒。计算条目的时间几乎没有变化。关于加快速度的任何建议?如果需要,我想我可以硬连线 dictnoC 的每个可能组合的条目数,这将用快速查找代替该步骤。 另一个提示:您可以使用 NSFetchedResultsController 只查询一次并保留它直到您的应用程序终止。这样你就可以从 fetchedObjects 属性中选择随机对象,将两个调用的时间减少到 ~0。 再次感谢您。我将研究 NSFetechedResultsController,或者我可以做类似的事情:当用户更改设置时,从主数据库中提取的线索子集很少更改。所以我可以把它放在一个数组中很长一段时间,然后随机抓取它。谢谢你的帮助!接受答案。

以上是关于SQL 与核心数据的真实世界性能:需要透视和可能的特定修复的主要内容,如果未能解决你的问题,请参考以下文章

性能测试的思维没打开,很多同学可能一开始就错了(内附真实项目性能测试报告)

Spark核心技术原理透视一(Spark运行原理)

Pandas+ SLS SQL:融合灵活性和高性能的数据透视

SQL Server 转置数据 - 可能进行数据透视?

真实的世界,真实的自我需要自我验证与亲自体验

真实世界中的 Swift 性能优化