【问题标题】:iOS Implementation TheoryiOS实现理论
【发布时间】:2012-08-07 18:30:56
【问题描述】:

我们(工作中的 IT 部门)正在寻求构建一个 iPad 应用程序,该应用程序将采用数字 ID 并在表格中提供简单的查找。它本质上是对单个表的主键搜索,并在小处理后显示一个字段。

注意事项

该表有 450 万行,查找时间最长为 1 秒。它没有互联网连接,因此必须在设备上进行。我们有一些想法,但最有意义的是:

  1. Sqlite:它能经受住这样的滥用吗?它可以处理那么多行吗,它会做得好吗?

  2. 平面文件搜索:我们可以自己循环文件,或者将它们按前几位数字拆分,以进行更智能的索引。

  3. 卸载到设备上可以通过 API 处理的某些第 3 方数据库应用程序。

  4. 在我们无限的智慧中我们完全错过了其他东西。

我必须借此机会感谢苹果让我们能够如此轻松地测试自己。如果没有 Mac 或 Dev 许可证,我们不想投入超过 2000 英镑,直到我们知道我们可以把它做好。

【问题讨论】:

  • 你真的尝试过这样做吗?将 450 万条记录表加载到 Core Data 中,然后在 iPad 上打开一个测试应用程序以查看您获得结果的速度需要几个小时。您将真正了解数据的大小和性能,无需猜测。
  • 查看问题的最后一句话。我们有一台 iPad,但没有开发许可证或 Mac,得到一台不是问题,但不仅仅是用于测试。我也应该说,这些都是小记录,
  • 您可以请拥有开发帐户和 iPad 的人来构建和安装它,看看结果如何。或者让他们将您的设备添加到他们的开发帐户,然后他们可以将构建发送给您,以便您自己查看它的外观。
  • 您有开发帐户和 iPad 吗? :) 只是开玩笑,我们只是想知道我们遇到的任何严重障碍。鉴于对软件的灵活性更有信心,我们可能会选择它。
  • 如果您想向我发送记录的 csv - 或其中合理大小的样本,我将为您敲出一个测试应用程序 - 我有类似的旧版本可以重新分配任务,让您了解响应时间

标签: ios database sqlite flat-file


【解决方案1】:

虽然 Sqlite 应该可以正常工作,但它可能有点矫枉过正。您只需要一个简单的二进制搜索即可。如果在一个大文件上速度太慢,请按前两位数将其拆分为 10 或 100 个子文件。

或者,您可以将数据加载到支持二进制搜索的CFArray 中(请参阅CFArrayBSearchValues)。虽然这会对初始加载造成性能损失,但它在后续搜索中的性能可能比对一个或多个文件的自定义二分搜索更好。

【讨论】:

  • 与其实现二分搜索,不如使用字典。
  • 我们考虑过这一点,但记录的绝对数量会导致问题吗?如果我们有 450 万行(非常小的记录,只是很多),优化内存管理是否足够容易
  • 是的,加载所有数据时这可能是个问题,我不知道。但是文件访问方法应该消耗很少的内存。
  • 因此,如果我们将记录拆分为 100 个文件(前两位数)并且仅根据请求进行搜索(非常频繁)。这是否仍然能够在一秒钟内返回(约 45,000 行随机分布)。驱动器访问(我知道的闪存)是否足够快?
  • 450 万行太多,无法加载到内存中!假设他们有一个只消耗 10 个字节的小结构,那将是 45MB 的内存,这已经太多了。查询是完全随机的吗?因为在每次访问新文件时将内容拆分到不同文件中都会引入巨大的过载,但如果您可以预测它或以执行最少读取量的方式对其进行排序,那就足够了。
【解决方案2】:

SQLite 速度惊人。包含 450 万条记录的测试表具有这种结构:

CREATE TABLE testtable (numericid INTEGER PRIMARY KEY, testtext TEXT);

其中填充了 numericid (0, 1, .... ) 的递增值和 testtext 的字符串。

在 MacBook Pro(2009 年)上以原子方式完成所有插入需要 1 小时 42 分钟。生成的 SQLite 文件大小为 94 MB。

在 iOS 应用程序中,数据库在 viewDidLoad 方法中打开。一个简单的按钮触发数据库查询,如下所示:

- (void)btnPressed:(UIButton *)sender{

    NSLog(@"btn pressed, start");

    sqlite3_stmt *statement = nil;

    NSString *querystring;

    querystring= [NSString stringWithFormat:@"SELECT * FROM testtable WHERE numericid = 2571312;"];  

    const char *sql = [querystring UTF8String];

    NSLog(@"sql is: %s", sql);

    if (sqlite3_prepare_v2(dbConnection, sql, -1, &statement, NULL)!=SQLITE_OK){

        NSLog(@"sql problem occured with: %s", sql);
        NSLog(@"%s", sqlite3_errmsg(dbConnection));

    }
    else
    {

        while (sqlite3_step(statement) == SQLITE_ROW) {            

            NSString *numericid = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
            NSString *testtext = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)];
            NSLog(@"%@",[NSString stringWithFormat:@"%@ (%@)", numericid, testtext]);

        } // while        

    }

    sqlite3_finalize(statement);    

    NSLog(@"btn pressed, finished");    

}

导致输出:

2012-08-10 17:51:36.734 DBQueryTest[28462:707] Database Successfully Opened
2012-08-10 17:51:39.083 DBQueryTest[28462:707] btn pressed, start
2012-08-10 17:51:39.087 DBQueryTest[28462:707] sql is: SELECT * FROM testtable WHERE numericid = 2571312;
2012-08-10 17:51:39.099 DBQueryTest[28462:707] text2571312 (2571312)
2012-08-10 17:51:39.102 DBQueryTest[28462:707] btn pressed, finished

所以查询需要 19 毫秒!这可以针对 numericid 的多个值重现,尽管我没有进行完全随机的统计评估测试。

结论:此测试设置满足您的要求。 SQLite 绝对是一条路。

更新:

具有 100000 个键值的快速随机访问测试验证了第一个结果。撇开 sql 语句字符串创建和耗时的 NSLog 输出超出时间测量,平均数据库查询时间下降了一个数量级:

平均查询时间:1.8 毫秒

平均偏差:0.4 ms

最长查询时间:25.9 毫秒

最短查询时间:0.6 毫秒

【讨论】:

  • 太棒了,非常感谢。它比我对内存少的 sqlite 的预期要好得多。
【解决方案3】:

正如我在评论中已经说过的那样 - 这并不难测试,您可以将大型数据库加载到 Core Data 存储中并创建一个测试应用程序以查看返回结果的速度。

我说 Core Data over SQLite - 因为它针对平台进行了优化(即使它使用 SQLite 作为存储介质),并且更容易编写返回值并显示它们的代码。

编辑添加

我创建了一个示例项目,该项目加载了一个包含 11 条记录和 400,000 条记录的数据集。这在 iOS 5 上使用了 Core Data。

在我的 iPad2 上运行测试(是 2)

搜索时间在 3-8 毫秒(0.003 - 0.008 秒)之间变化,较小和较大的数据集之间没有明显差异。

这是未经优化的代码,在调试模式下运行,并且不是为任何类型的性能增强而编写的 - 搜索谓词是在每次搜索时创建的,而不是缓存的,例如,没有线程。

400,000 条记录的数据存储大小为 17.2 mb,因此即使是更大的 450 万条记录也可以轻松容纳在 iPad 上。

【讨论】:

    【解决方案4】:

    在 iOS 上将此数据加载到内存中是行不通的。

    您应该使用 SQLLite。这就是它的用途,您不会更好地处理文件 IO 代码。

    【讨论】:

    • 似乎非常明确,你有关于为什么记忆是不行的数字吗?或者为什么 SQLites 处理更好,它不需要将它们加载到内存中进行搜索吗?
    • 在 iOS 上工作了一段时间后,我会说常识表明,将 450 万个内容加载到移动设备的内存中可能是个坏主意。您的意见可能会有所不同。
    • Sqlite 专为资源最少的环境而设计。如果它将整个表加载到内存中进行查询,那么我猜我错了。
    • 是的,这似乎是一个非常糟糕的主意,但我们可以做出妥协,如果这意味着应用程序需要 5 分钟加载和 5 分钟再次关闭,这实际上不是问题对我们来说,这是我们需要的应用程序内的响应能力。因此,硬数据对于评估折衷方案很有用。但是是的,我明白你的意思,SQLite 会比手动滚动更好,但这并不能使 SQLite 变得更好。
    • 其实是个问题。移动应用程序旨在不垄断设备本身的资源。如果您不关心将其构建为移动规范,我不确定您为什么问这个问题。任何人都可以构建一个消耗尽可能多资源的应用程序。当一个 iPad 应用程序需要 5 分钟才能启动时,在一秒钟内返回一个查询是一个有趣的位置。如果任何应用程序需要很长时间才能启动,用户会(正确地)假设设备已挂起并终止应用程序。
    猜你喜欢
    • 2018-03-02
    • 2017-12-10
    • 2017-04-12
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 2012-12-31
    相关资源
    最近更新 更多