【问题标题】:How to efficient insert and fetch UUID in Core Data如何在 Core Data 中高效地插入和获取 UUID
【发布时间】:2012-07-05 10:39:20
【问题描述】:

我正在寻找一种在 Core Data 中存储和搜索 UUID 的有效方法。这些 UUID 是由分布式系统中的许多 iOS 设备生成的。这些设备中的每一个都可以存储大约 20-50k 个 UUID。

很明显,在 Core Data 中将 UUID 存储为 String 会损害对其进行索引的效率。但经过一系列研究后,我发现将 UUID 存储为 Core Data 中的二进制数据(并对其进行索引)可能不如将其存储为 String 有效

由于 SQLit 中不支持类似 BINARY 或类似 VARBINARY 的数据类型。我猜 Core Data 中的任何二进制数据类型的数据都在 SQLit 中存储为 BLOB。由于 BLOB 可能是索引速度最慢的数据类型,因此会对性能造成不良影响。

那么任何人都可以帮忙回答,有没有更有效的方法将 UUID 存储在 Core Data 中?

【问题讨论】:

  • 您知道从 iOS 5 开始,对 UDID 的访问权限已被弃用,是吗?
  • OP 正在谈论与 UDID 不同的 UUID。
  • @JodyHagins,你是对的。我提到的 UUID 是我的应用程序创建的 ManagedObjects 的通用唯一标识符。

标签: ios swift sqlite core-data uuid


【解决方案1】:

将它们存储为 ASCII 字符串,并使字段成为索引。

编辑

Egads,我碰巧在闲逛,偶然发现了这个。多么可耻的回答。那天我一定是有点心情。如果可以的话,我会删除它并继续前进。但是,这是不可能的,所以我将提供一个更新的片段。

首先,了解什么是“高效”的唯一方法是衡量,同时考虑程序时间和空间以及源代码复杂性和程序员工作量。

幸运的是,这很容易。

我写了一个非常简单的 OSX 应用程序。该模型由一个属性组成:identifier

如果您不将属性标记为索引,这些都不重要。创建商店会花费更多时间,但它会使查询速度更快。

另外,请注意,为二进制属性创建谓词与为字符串创建谓词完全相同:

fetchRequest.predicate =
    [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

应用程序非常简单。首先,它创建 N 个对象,并为标识符属性分配一个 UUID。它每 500 个对象保存一次 MOC。然后我们将所有标识符存储到一个数组中并随机打乱它们。然后将整个 CD 堆栈完全拆除以将其全部从内存中删除。

接下来,我们再次构建堆栈,然后遍历标识符,并进行简单的获取。构造 fetch 对象,使用一个简单的谓词来获取该对象。所有这些都是在自动释放池中完成的,以尽可能保持每次提取的原始状态(我承认会与 CD 缓存进行一些交互)。这并不重要,因为我们只是比较不同的技术。

二进制标识符是 UUID 的 16 字节。

UUID String是一个36字节的字符串,调用[uuid UUIDString]的结果,看起来是这样的(B85E91F3-4A0A-4ABB-A049-83B2A8E6085E)。

Base64 String 是一个 24 字节的字符串,对 16 字节 UUID 二进制数据进行 base-64 编码的结果,对于相同的 UUID,它看起来像这样 (uF6R80oKSrugSYOyqOYIXg==)。

Count 是该运行的对象数。

SQLite 大小是实际 sqlite 文件的大小。

WAL 大小是 WAL(预写日志)文件的大小 - 仅供参考...

create是创建数据库的秒数,包括保存。

Query 是查询每个对象的秒数。

Data Type     | Count (N) | SQLite Size | WAL Size  | Create  | Query
--------------+-----------+-------------+-----------+---------+---------
Binary        |   100,000 |   5,758,976 | 5,055,272 |  2.6013 |  9.2669
Binary        | 1,000,000 |  58,003,456 | 4,783,352 | 59.0179 | 96.1862
UUID String   |   100,000 |  10,481,664 | 4,148,872 |  3.6233 |  9.9160
UUID String   | 1,000,000 | 104,947,712 | 5,792,752 | 68.5746 | 93.7264
Base64 String |   100,000 |   7,741,440 | 5,603,232 |  3.0207 |  9.2446
Base64 String | 1,000,000 |  77,848,576 | 4,931,672 | 63.4510 | 94.5147

首先要注意的是,实际的数据库大小远大于存储的字节数(1,600,000 和 16,000,000)——这对于数据库来说是意料之中的。额外存储的数量在某种程度上与实际对象的大小有关……这个只存储标识符,因此开销百分比会更高)。

其次,关于速度问题,作为参考,执行相同的 1,000,000 个对象查询,但在 fetch 中使用 object-id 大约需要 82 秒(请注意,这与调用 existingObjectWithID:error: 之间的明显差异需要高达 0.3065秒)。

您应该分析您自己的数据库,包括在运行代码上明智地使用工具。我想如果我进行多次运行,数字会有所不同,但它们非常接近,因此没有必要进行此分析。

但是,根据这些数字,让我们看看代码执行的效率测量。

  • 正如预期的那样,存储原始 UUID 二进制数据在空间方面效率更高。
  • 创建时间非常接近(差异似乎取决于创建字符串的时间和所需的额外存储空间)。
  • 查询时间似乎几乎相同,二进制字符串似乎有点慢。我认为这是最初的担忧——对二进制属性进行查询。

二进制大大赢得了空间,它可以被认为在创建时间和查询时间上都非常接近。如果我们只考虑这些,存储二进制数据显然是赢家。

源代码复杂度和程序员时间如何?

好吧,如果您使用的是现代版本的 iOS 和 OSX,则几乎没有区别,尤其是 NSUUID 上的简单类别。

但是,您需要考虑一个因素,那就是使用数据库中的数据是否容易。存储二进制数据时,很难对数据进行良好的可视化。

因此,如果出于某种原因,您希望数据库中的数据以对人类更有效的方式存储,那么将其存储为字符串是更好的选择。因此,您可能需要考虑使用 base64 编码(或其他一些编码——尽管记住它已经在 base-256 编码中)。

FWIW,这是一个示例类别,可以更轻松地以 NSData 和 base64 字符串的形式访问 UUID:

- (NSData*)data
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    return [NSData dataWithBytes:rawuuid length:sizeof(rawuuid)];
}

- (NSString*)base64String
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    NSData *data = [NSData dataWithBytesNoCopy:rawuuid length:sizeof(rawuuid) freeWhenDone:NO];
    return [data base64EncodedStringWithOptions:0];
}

- (instancetype)initWithBase64String:(NSString*)string
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    if (data.length == sizeof(uuid_t)) {
        return [self initWithUUIDBytes:data.bytes];
    }
    return self = nil;
}

- (instancetype)initWithString:(NSString *)string
{
    if ((self = [self initWithUUIDString:string]) == nil) {
        self = [self initWithBase64String:string];
    }
    return self;
}

【讨论】:

  • 好建议,谢谢。它可能会为 Core Data 节省一半的工作量。但我仍然想知道 ASCII 字符串是如何从 Core Data 映射到 SQLit 的。我想只有运行真正的测试才能知道。
  • 一般来说,您希望确保您搜索的任何字符串都经过规范化以排除 unicode。此外,不要使用不区分大小写的搜索,而是规范化数据以删除 unicode 和大小写。使用 代替 BEGINSWITH 等。在 2010、2011 和 2012 年的 WWDC 视频中有很好的建议。我强烈推荐他们。
  • 嗨 @JodyHagins 你能指定关于这个主题的 WWDC 视频的名称吗?他们太多了。提前致谢。
  • 嗨@JodyHagins 我尝试使用 NSString *utf8String = [NSString stringWithFormat:@"%c%c%c%c%c%c%c%c%c%c 将 UUID 字节保存在字符串中%c%c%c%c%c%c", uuidBytes.byte0, uuidBytes.byte1, ...... uuidBytes.byte15];但事实证明这是一种不可靠的方式。值发生变化。那么你能推荐一种将 UUID 转换为 ASCII 字符串的好方法吗?
  • Cable - 生成 UUID 并使其成为字符串,使用如下代码 (ARC):CFUUIDRef uuidRef = CFUUIDCreate(NULL); NSString *uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuidRef); CFRelease(uuidRef);
【解决方案2】:

由于这篇文章似乎相当受欢迎,值得注意的是,自 2012 年以来情况发生了一些变化。

您现在可以使用 NSUUIDA/UUID 属性类型 (UUIDAttributeType) 而不是手动将其映射到字符串或二进制数据(在 iOS 11 中添加)。 UUID 将自动存储为二进制,根据另一个答案,这是在 CoreData 中存储 UUID 的最快、最佳方式。

WWDC17: What's New in Core Data

[20:21] 我们添加了分别由 UUID 和 URL 值类支持的 NSUUIDA 属性类型和 NSURL 属性类型。

【讨论】:

    猜你喜欢
    • 2017-08-06
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    相关资源
    最近更新 更多