如何在Core Data中有效地插入和获取UUID

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

很显然,将UUID作为string存储在核心数据中会损害索引的效率。 但经过一系列的研究后,我发现将UUID作为二进制数据存储在核心数据(和索引它)可能不如string存储效率

由于在SQLit中不支持类似BINARY或VARBINARY的数据types。 我猜测核心数据中的任何二进制数据types的数据在SQLit中存储为BLOB。 由于BLOB可能是最慢的数据types被索引,会对性能造成不良影响。

那么任何人都可以帮助回答,有没有一种更有效的方式来存储UUID在核心数据?

将它们存储为ASCIIstring,并将该字段设置为索引。

编辑

Egads,我碰巧正在做点什么,碰到这个。 多么可耻的答案。 那天我肯定有点心情不好。 如果可以的话,我只是删除它,继续前进。 但是,这是不可能的,所以我会提供一个更新的剪辑。

首先,要知道什么是“高效”的唯一方法就是考虑程序的时间和空间,以及源代码的复杂性和程序员的努力。

幸运的是,这个很容易。

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

如果你不把你的属性标记为索引,这些都不重要。 创build商店需要花费很多时间,但会使查询速度更快。

另外,请注意,为二进制属性创build谓词与为string创build谓词完全相同:

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

该应用程序非常简单。 首先,它创buildN个对象,并为标识符属性分配一个UUID。 它每500个物体就保存MOC。 然后,我们将所有的标识符存储到一个数组中,并随机地将其混合。 整个CD堆被完全撕下,将其全部从内存中移除。

接下来,我们再次构build堆栈,然后迭代标识符,并进行简单的获取。 获取对象被构造,用一个简单的谓词来获取这个对象。 所有这些都是在autoreleasepool内完成的,以保持每个抓取尽可能原始(我承认会有一些与CDcaching的交互)。 这并不重要,因为我们只是比较不同的技术。

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

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

Base64string是一个24字节的string,base-64编码16字节的UUID二进制数据的结果,它看起来像这样(uF6R80oKSrugSYOyqOYIXg ==)为相同的UUID。

计数是该运行的对象的数量。

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

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

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

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

 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个对象查询,但是在获取中使用object-id需要大约82秒(注意与调用existingObjectWithID:error:之间的明显区别existingObjectWithID:error:花了0.3065秒) 。

您应该分析自己的数据库,包括在正在运行的代码上正确使用仪器。 我想如果我做了多次运行,这些数字会有所不同,但是它们非常接近,所以没有必要进行这种分析。

但是,基于这些数字,我们来看代码执行的效率度量。

  • 正如所料,存储原始UUID二进制数据在空间方面更为有效。
  • 创build时间非常接近(差异似乎是基于创buildstring的时间和所需的额外存储空间)。
  • 查询时间似乎几乎完全相同,二进制string似乎慢了一点。 我认为这是最初的担心 – 对二进制属性进行查询。

Binary赢得了很多空间,可以被认为是创造时间和查询时间的一个缩影。 如果我们只考虑这些,那么存储二进制数据就是明显的赢家。

如何处理源代码的复杂性和程序员的时间?

那么,如果你使用的是iOS和OSX的现代版本,那么几乎没有什么区别,特别是在NSUUID上有一个简单的类别。

但是,您有一个需要考虑的问题,那就是在数据库中使用数据的方便性。 当您存储二进制数据时,很难获得良好的数据视觉效果。

因此,如果出于某种原因,希望数据库中的数据以更有效的方式存储在人身上,那么将其存储为string是更好的select。 所以,你可能要考虑一个base64编码(或其他编码 – 尽pipe记住它已经在base-256编码)。

FWIW,下面是一个示例类别,提供对NSData和base64string的更简单的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; }