iphone - 为什么在 iOS6 上访问 SQLite 数据库需要更长的时间?

标签 iphone ios database sqlite fmdb

在我的 iPhone 应用程序上,我使用的是 SQLite 数据库,为此我依赖于 fmdb . 在装有 iOS 5 的 iPhone 上,初始数据库访问最多需要 10 秒,但在 iOS 6 上需要 30 到 2 分钟 40 秒(在 iPhone 4 和 4S 上,在 iPhone 5 上大约需要 15-20 秒)。 OIne 我注意到时间总是一致的,如果在一部手机上需要 60 秒,它总是需要 ~60 秒。

为什么在 iOS 6 中使用 fmdb 比在 iOS 5 中慢?有比 fmdb 更好的替代品吗?

就代码而言,这是我正在做的示例:

获取数据

+ (NSMutableArray*) getLevadas
{
    FMDatabase *db = [DBAdapter getDB];
    if([db open])
    {
        FMResultSet *result = [db executeQuery:@"SELECT Levada.Levada_id, Levada.Nome, a.Nome as Inicio, b.Nome as Fim, Dificuldade.NomePT as DificuldadePT, Dificuldade.NomeES as DificuldadeES, Dificuldade.NomeEN as DificuldadeEN, Altitude, Distancia, DescricaoPT, DescricaoEN, DescricaoES, Latitude, Longitude, Autocarro, Levada.dataModificado, a.Sector, Levada.Duracao, b.Sector as SectorFim, GROUP_CONCAT(DISTINCT PalavrasChave.NomePT) as PalavrasChavePT, GROUP_CONCAT(DISTINCT PalavrasChave.NomeEN) as PalavrasChaveEN, GROUP_CONCAT(DISTINCT PalavrasChave.NomeES) as PalavrasChaveES, GROUP_CONCAT(DISTINCT Equipamento.NomeEN) as EquipamentoEN, GROUP_CONCAT(DISTINCT Equipamento.NomePT) as EquipamentoPT, GROUP_CONCAT(DISTINCT Equipamento.NomeES) as EquipamentoES, Levada.Coordenadas, Levada.LongitudeFim, Levada.LatitudeFim, Levada.PontosInteresse, GROUP_CONCAT(DISTINCT Fotos.url) as Fotos FROM Levada, Localizacoes as a, Localizacoes as b, Dificuldade, Levada_has_PalavrasChave, PalavrasChave, Levada_has_Equipamento, Equipamento, Fotos WHERE ((Inicio_id = a.Localizacoes_id) AND (Fim_id = b.Localizacoes_id) AND (Levada.Dificuldade_id = Dificuldade.Dificuldade_id) AND (Levada.Levada_id = Levada_has_PalavrasChave.Levada_id) AND (PalavrasChave.PalavrasChave_id = Levada_has_PalavrasChave.PalavrasChave_id) AND (Levada.Levada_id = Levada_has_Equipamento.Levada_id) AND (Levada_has_Equipamento.Equipamento_id = Equipamento.Equipamento_id) AND Levada.Levada_id = Fotos.idLevada) GROUP BY Levada.Levada_id ORDER BY Levada.Nome;"];

        NSMutableArray *levadas = [[NSMutableArray alloc] init];

        while ([result next])
        {
            Levada *l = [[Levada alloc] init];
            [l ID:[NSNumber numberWithInt: [result intForColumn:@"Levada_id"]]];
            [l Nome:[result stringForColumn:@"Nome"]];
            [l Dificuldade:[result stringForColumn:[NSString stringWithFormat:@"Dificuldade%@", NSLocalizedString(@"lingua", NULL)]]];
            [l Distancia:[result objectForColumnName:@"Distancia"]];
            [l Duracao:[result stringForColumn:@"Duracao"]];
            [l Inicio:[result stringForColumn:@"Inicio"]];
            [l Fim:[result stringForColumn:@"Fim"]];
            [l Altitude:[result stringForColumn:@"Altitude"]];
            [l PalavrasChave:[[result stringForColumn:[NSString stringWithFormat:@"PalavrasChave%@", NSLocalizedString(@"lingua", NULL)]] stringByReplacingOccurrencesOfString:@"," withString:@", "]];
            [l Descricao:[result stringForColumn:[NSString stringWithFormat:@"Descricao%@", NSLocalizedString(@"lingua", NULL)]]];
            [l Fotos: [[result stringForColumn:@"Fotos"] componentsSeparatedByString:@","]];
            [l Latitude:[NSNumber numberWithDouble:[[result stringForColumn:@"Latitude"] doubleValue]]];
            [l LatitudeFim:[NSNumber numberWithDouble:[[result stringForColumn:@"LatitudeFim"] doubleValue]]];
            [l Longitude:[NSNumber numberWithDouble:[[result stringForColumn:@"Longitude"] doubleValue]]];
            [l LongitudeFim:[NSNumber numberWithDouble:[[result stringForColumn:@"LongitudeFim"] doubleValue]]];
            [l Coordenadas:[result stringForColumn:@"Coordenadas"]];
            [l Equipamento: [[result stringForColumn:[NSString stringWithFormat:@"Equipamento%@", NSLocalizedString(@"lingua", NULL)]] componentsSeparatedByString:@","]];
            [l DataModificado:[result stringForColumn:@"dataModificado"]];
            [l Sector: [result stringForColumn:@"Sector"]];
            [l SectorFim: [result stringForColumn:@"SectorFim"]];
            [l Autocarro: [result stringForColumn:@"Autocarro"]];
            [levadas addObject:l];
        }
        [result close];
        [db close];
        return levadas;
    }
    return nil;
}

打开数据库

+ (FMDatabase*) getDB
{
    [DBAdapter copyDatabaseIfNeeded];
    FMDatabase *db = [FMDatabase databaseWithPath:[DBAdapter getDBPath]];
    [db setLogsErrors:YES];
    return db;
}
+ (void) copyDatabaseIfNeeded
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;
    NSString *dbPath = [DBAdapter getDBPath];
    BOOL success = [fileManager fileExistsAtPath:dbPath];

    if(!success)
    {
        NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"WalkMe.sqlite"];
        success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
    }
}
+ (NSString *) getDBPath
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDir = [paths objectAtIndex:0];
    return [documentsDir stringByAppendingPathComponent:@"WalkMe.sqlite"];
}

更新:

这是时间分析的屏幕截图:

time profiling

据我所知,最耗时的是 [FMResultSet next] 指令,更具体地说是在 FMDB 下一个代码中的 rc = sqlite3_step([_statement statement]); 上。所以我猜问题真的出在 SELECT 语句上。

更新 2:

我尝试将 SQL 语句简化为 SELECT Levada_id FROM Levada,但它在 [FMResultSet next] 中仍然花费太多时间 始终清理您的项目.

最佳答案

在花了一些时间详细查看 FMDB 代码后,没有任何内容可以解释这些类型的延迟。 FMDB 是一个非常薄的包装器,几乎可以肯定不对行为负责。那里也有足够多的 SQLite/FMDB 用户,如果 iOS 6 中真的存在与 SQLite/FMDB 相关的问题,我会感到非常惊讶。我原以为如果有的话,我们现在应该已经听到很多关于它的消息了。

但是看一眼您的 SELECT 语句,这似乎相当复杂,如果我打赌,如果问题实际上与数据库相关,我敢打赌这就是延迟的根源。如果您单步执行代码,这将很容易验证。或者在前后放入 NSLog 语句,您将获得带时间戳的日志语句,这说明了问题。

归根结底,假设问题实际上与数据库有关,那几乎可以肯定是 SQLite 问题。如果您重写此函数以使用 sqlite3_xxx() 调用执行所有操作,您可能会遇到相同的性能问题。话虽如此,我还没有看到 FMDB/SQLite 在迁移到 iOS6 时出现任何性能问题。我还以为,如果 iOS 6 上的 SQLite/FMDB 存在一些广泛的性能问题,我们会听到令人毛骨悚然的嚎叫。

我会专注于通过 SQLite 的 EXPLAIN 放置该 sql 语句,然后优化表,确保您有适当的索引或以其他方式重构 SQL。只需在计算机上的 SQLite 中打开数据库,然后尝试分析那里的查询。

关于在 iOS 6 和 iOS 5 上可能导致此类性能问题的原因,我不熟悉它打开数据库的方式的任何变化。

听听您的分析是否确认了 SQLite 的问题,或者它是否与 SQLite/FMDB 完全无关。


更新:

我决定检查一下 iOS 5.1.1 和 6.0.1 中的 SQLite 配置之间是否有任何有趣的配置更改,但我没有发现任何问题。 (不同的线程模型肯定会影响性能,但它们看起来是一样的。)iOS 6 肯定使用了稍微更新的 SQLite 版本,但我怀疑这会对它产生负面影响。


Device: iPhone OS
iOS: 5.1.1
SQLite Configuration:
  Version: 3.7.7
  Threadsafe: Yes
  Options:
    ENABLE_FTS3: Yes
    ENABLE_FTS3_PARENTHESIS: Yes
    ENABLE_LOCKING_STYLE=1: Yes
    ENABLE_RTREE: Yes
    OMIT_AUTORESET: Yes
    OMIT_BUILTIN_TEST: Yes
    OMIT_LOAD_EXTENSION: Yes
    TEMP_STORE=1: Yes
    THREADSAFE=2: Yes
Device: iPhone OS
iOS: 6.0.1
SQLite Configuration:
  Version: 3.7.13
  Threadsafe: Yes
  Options:
    CURDIR: Yes
    ENABLE_FTS3: Yes
    ENABLE_FTS3_PARENTHESIS: Yes
    ENABLE_LOCKING_STYLE=1: Yes
    ENABLE_RTREE: Yes
    OMIT_AUTORESET: Yes
    OMIT_BUILTIN_TEST: Yes
    OMIT_LOAD_EXTENSION: Yes
    TEMP_STORE=1: Yes
    THREADSAFE=2: Yes

关于iphone - 为什么在 iOS6 上访问 SQLite 数据库需要更长的时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13711372/

相关文章:

iPhone - 如何为客户提交申请

ios - WatchOS2 Watch Connectivity 仍然需要功能中的应用程序组吗?

ios - 带有 xcode 5.1 的 cordova 3.4 不会使用最新的文件传输插件构建

mysql - 在查询中重命名值

iPhone - 单独文件上的一般方法

iphone - 在 Objective-C 中,如何声明/使用全局变量?

iphone - 使用 GZIP 或 deflate 在 objective-c (iphone) 中压缩/解压缩 NSString

ios - 测量蜂窝信号强度

database - 用于社交网络应用程序的 Memcached

php - php 文档中未选择数据库