Подтвердить что ты не робот

Как эффективно вставлять и получать UUID в основных данных

Я ищу эффективный способ хранения и поиска UUID в Core Data. Эти UUID генерируются многими устройствами iOS в распределенной системе. Каждое из этих устройств может хранить около 20-50 тыс. UUID.

Очевидно, что сохранение UUID в виде строки в базовых данных может повредить эффективность индексации на нем. Но после серии исследований я обнаружил, что сохранение UUID в виде двоичных данных в основных данных (и индексация) может быть менее эффективным, чем сохранение его как String.

Поскольку в SQLit не поддерживается тип BINARY-like или VARBINARY-подобный тип данных. Я предполагаю, что любой тип данных двоичных данных в Core Data хранится как BLOB в SQLit. Поскольку BLOB может быть самым медленным типом данных для индексирования, это будет плохо влиять на производительность.

Так может кто-нибудь помочь ответить, есть ли более эффективный способ хранения UUID в Core Data?

4b9b3361

Ответ 1

Сохраните их как строку ASCII и сделайте поле индексом.

ИЗМЕНИТЬ

Эгады, я, случалось, что-то выкапывал, и наткнулся на это. Какой позорный ответ. В тот день, должно быть, у меня было настроение. Если бы я мог, я бы просто удалил его и продолжил. Тем не менее, это невозможно, поэтому я расскажу об обновлении.

Во-первых, единственный способ узнать, что является "эффективным", - это измерить, учитывая время и пространство программы, а также сложность исходного кода и работу программиста.

К счастью, это довольно просто.

Я написал очень простое приложение OSX. Модель состоит из одного атрибута: identifier.

Ничего из этого не имеет значения, если вы не отмечаете свой атрибут как индекс. При создании магазина потребуется гораздо больше времени, но он будет делать запросы намного быстрее.

Также обратите внимание, что создание предиката для двоичного атрибута точно такое же, как создание одного для строки:

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

Приложение очень простое. Во-первых, он создает N объектов и присваивает UUID атрибуту идентификатора. Он сохраняет MOC каждые 500 объектов. Затем мы сохраняем все идентификаторы в массив и произвольно перетасовываем их. Затем весь CD-стоп полностью разорван, чтобы удалить все из памяти.

Затем мы снова создаем стек, а затем перебираем идентификаторы и делаем простую выборку. Объект fetch построен с простым предикатом для извлечения этого одного объекта. Все это делается внутри autoreleasepool, чтобы каждый выбор был как можно более чистым (я признаю, что будет некоторое взаимодействие с кэшами CD). Это не так важно, поскольку мы просто сравниваем различные методы.

Двоичный идентификатор - это 16-байты для UUID.

UUID String - это 36-байтовая строка, результат вызова [uuid UUIDString], и это выглядит так (B85E91F3-4A0A-4ABB-A049-83B2A8E6085E).

Base64 String - это 24-байтная строка, результат базы-64, кодирующей 16-байтные двоичные данные UUID, и выглядит так: (uF6R80oKSrugSYOyqOYIXg ==) для того же UUID.

Count - это количество объектов для этого запуска.

Размер SQLite - это размер фактического файла sqlite.

Размер WAL - это размер файла WAL (запись на запись вперед) - только FYI...

Создать - это количество секунд для создания базы данных, включая сохранение.

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 объектных запросов, но с использованием идентификатора объекта в выборке потребовалось около 82 секунд (обратите внимание на резкую разницу между этим и вызовом existingObjectWithID:error:, которая взяла колоссальный 0,3065 секунд).

Вы должны профилировать свою собственную базу данных, включая разумное использование инструментов в текущем коде. Я предполагаю, что цифры будут несколько отличаться, если я сделал несколько прогонов, но они настолько близки, что это не нужно для этого анализа.

Однако, исходя из этих чисел, давайте посмотрим на измерения эффективности для выполнения кода.

  • Как и ожидалось, сохранение исходных двоичных данных UUID более эффективно с точки зрения пространства.
  • Время создания довольно близко (разница заключается в том, что время для создания строк и дополнительное пространство для хранения требуется).
  • Время запроса кажется почти идентичным, при этом двоичная строка выглядит немного медленнее. Я думаю, что это была оригинальная проблема - выполнение запроса по двоичному атрибуту.

Двоичный выигрывает пространство по большому количеству, и его можно считать близкой чертой как времени создания, так и времени запроса. Если мы просто рассмотрим их, то сохранение двоичных данных станет явным победителем.

Как насчет сложности исходного кода и времени программиста?

Хорошо, если вы используете современную версию iOS и OSX, практически нет никакой разницы, особенно с простой категорией на NSUUID.

Однако для вас есть одно соображение и простота использования данных в базе данных. Когда вы храните двоичные данные, трудно получить хороший визуал данных.

Итак, если по какой-то причине вы хотите, чтобы данные в базе данных были сохранены более эффективным образом для людей, то сохранение его в виде строки является лучшим выбором. Таким образом, вы можете рассмотреть кодировку base64 (или некоторую другую кодировку, хотя помните это уже в кодировке base-256).

FWIW, вот примерная категория, обеспечивающая более легкий доступ к UUID как строки NSData и base64:

- (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;
}