Улучшить процесс зеркалирования базы данных сервера в клиентскую базу данных через JSON?

У меня есть готовое корпоративное приложение (non-AppStore) для iOS для iPad, которое мне нужно для рефакторинга (он был написан другим разработчиком, моим предшественником в моей текущей работе).

Это приложение извлекает свои данные через JSON с сервера, имеющего базу данных MSSQL. В схеме базы данных имеется около 30 таблиц, наиболее емкими являются: Клиент, Город, Агентство, каждый из которых имеет около 10.000 записей, а дальнейший рост ожидается в будущем. После получения JSON (одна пара запросов и ответа JSON для каждой таблицы) - она ​​сопоставляется с CoreData - процессом, который также включает в себя объединение друг с другом соответствующих объектов CoreData (Клиент, Город, Агентство и другие) друг с другом, т.е. устанавливая отношения между этими объектами на уровне CoreData.

Сам по себе проект-выборка CoreData (или часть read-part) сильно оптимизирован - он использует, я думаю, почти все возможные улучшения производительности и памяти CoreData, поэтому уровень приложения UI очень быстрый и отзывчивый, так что я считаю его работу полностью удовлетворительной и адекватной.


Проблема - это процесс подготовки уровня CoreData, то есть процесс синхронизации между сервером: требуется слишком много времени. Рассмотрим 30 сетевых запросов, в результате получивших 30 пакетов JSON ( "пакет", я имею в виду "одна таблица - один JSON" ), которые затем отображаются на 30 объектов CoreData, которые затем склеиваются (соответствующие отношения CoreData устанавливаются между ними). Когда я впервые увидел, как все это делается в этом проекте (слишком медленно), первая идея, которая появилась у меня в голове, заключалась в следующем:

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

Но потом я понял, что даже если такая передача такого однофакторного дампа была возможна, CoreData все равно потребует от меня выполнить склейку соответствующих объектов CoreData, чтобы установить соответствующие отношения между ними, так что трудно представить что я мог бы воспользоваться результатами, если бы я полагался на эту схему.

Кроме того, мой коллега предложил мне рассмотреть SQLite как полную альтернативу Core Data, но, к сожалению, у меня нет опыта его использования, поэтому я полностью слеп, чтобы предвидеть все последствия такого серьезного проектного решения ( даже если процесс синхронизации очень медленный, мое приложение действительно работает, особенно его производительность пользовательского интерфейса очень хороша сейчас). Единственное, что я могу себе представить о SQLite, который, в отличие от Core Data, не заставит меня приклеить дополнительные отношения на стороне клиента, потому что SQLite имеет свою старую систему внешнего ключа, не так ли?


Итак, вот вопросы (Респонденты, пожалуйста, не смешивайте эти моменты, когда вы отвечаете - слишком много путаницы у меня есть обо всех них):

  • Есть ли у кого-нибудь такой опыт применения подхода "первый раз крупный импорт всей базы данных" таким образом, как я описал выше? Я был бы очень благодарен за информацию о любых решениях, если они будут использовать пару JSON ↔ CoreData или нет.

  • Имеет ли Core Data некоторый глобальный механизм импорта, который может позволить массовое создание соответствующей схемы из 30 таблиц (возможно, используя какой-то конкретный источник, отличный от "30 пакетов JSON", описанный выше) без необходимости настройки соответствующие отношения для 30 объектов?

  • Есть ли возможности ускорить процесс синхронизации, если 2) невозможно? Здесь я имею в виду улучшения текущей схемы JSON ↔ CoreData, используемой моим приложением.

  • Переход на SQLite: следует ли рассматривать такую ​​миграцию? Что я получу от этого? Как может выглядеть весь процесс репликации → передача- > клиентские препараты?

  • Другие альтернативы CoreData и SQLite - что они могут быть или похожи?

  • Любые другие мысли или видения, которые могут возникнуть у вас о ситуации, описанной мной?


ОБНОВЛЕНИЕ 1

Хотя ответ, написанный Mundi, хорош (один большой JSON, "Нет" для использования SQLite), мне все еще интересно, есть ли какие-либо другие сведения о проблеме, которую я описал.


ОБНОВЛЕНИЕ 2

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

Пожалуйста, рассмотрите две дихотомии:

  • Что можно/следует использовать в качестве слоя данных для клиента iOS - CoreData vs SQLite?
  • Что можно/следует использовать в качестве транспортного уровня - JSON (один-JSON-at-once, как это было предложено в ответе, возможно, даже в zip файле) или какие-то самолеты DB (если это даже возможно, конечно) Обратите внимание, что я также задаю это в своем вопросе).

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

Сказав это, я заявляю, что буду благодарен за любые ответы, касающиеся пары CoreData-JSON, а также ответы, рассматривающие использование любых других "секторов" (как насчет выбора SQLite и какого-то своего подхода к дампам, почему?)

Также важно отметить, что я не хочу просто отбрасывать текущую опцию для некоторых других альтернатив, я просто хочу, чтобы решение быстро работало на этапах синхронизации и UI его использования. Поэтому ответы об улучшении текущей схемы, а также ответы, предлагающие другие схемы, приветствуются!

Теперь см. следующее обновление №3, в котором содержится более подробная информация о текущей ситуации CoreData-JSON:


ОБНОВЛЕНИЕ 3

Как я уже сказал, в настоящее время мое приложение получает 30 пакетов JSON - один пакет для всей таблицы. Давайте возьмем емкие таблицы, например: Client, Agency, City.

Это основные данные, поэтому, если в записи client есть непустое поле agency_id, мне нужно создать новый объект Core Data класса Agency (NSManagedObject subclass) и заполнить его данными записи JSON, поэтому мне нужно уже иметь соответствующий объект Core Data для этого агентства класса Agency (NSManagedObject subclass), и, наконец, мне нужно сделать что-то вроде client.agency = agency;, а затем вызвать [currentManagedObjectContext save:&error]. Сделав это, позже я могу попросить этого клиента получить его и попросить его свойство .agency найти соответствующий объект. Надеюсь, я полностью прав, когда я это делаю.

Теперь представьте, что этот шаблон применяется к следующей ситуации:

Я только что получил следующие 3 отдельных пакета JSON: 10000 клиентов и 4000 городов и 6000 агентств (у клиента есть один город, в городе много клиентов, у клиента есть агентство, агентство имеет много клиентов, агентство имеет один город, в городе много агентства).

Теперь я хочу установить следующие отношения на уровне основных данных: я хочу, чтобы мой клиентский объект client был подключен к соответствующему городу и соответствующему агентству.

Текущая реализация этого в проекте очень уродливая:

  • Так как порядок зависимостей следующий: City → Agency → Client i.e, сначала нужно запечь город, приложение начинает создавать объекты для Города и сохраняет их в Core Data.

  • Затем он имеет дело с JSON агентств: он выполняет итерацию через каждую запись JSON - для каждого агентства создается новый объект agency и его city_id, он извлекает соответствующий объект city и подключается он использует agency.city = city. После завершения итерации по всем массивам JSON агентства сохраняется текущий контекст управляемого объекта (фактически - [managedObjectContext save:] выполняется несколько раз, каждый после обработки 500 записей). На этом этапе очевидно, что получение одного из 4000 городов для каждого клиента для каждого из 6000 агентств имеет огромное влияние на производительность всего процесса синхронизации.

  • Затем, наконец, он касается JSON клиентов: как и в предыдущем 2-м этапе, он выполняет итерацию через весь массив JSON из 10000 элементов и один за другим выполняет выбор соответствующих агентств и городов ZOMG, и это влияет общая производительность аналогична предыдущему этапу 2.

Все это очень плохо.

Единственная оптимизация производительности, которую я вижу здесь, заключается в том, что первый этап может оставить большой словарь с идентификаторами городов (я имею в виду NSNumber реальных идентификаторов) и ошибочными объектами города как значениями), поэтому можно было бы предотвратить уродливые найтипроцесс следующего этапа 2, а затем сделать то же самое на этапе 3 с использованием аналогичного кэширующего трюка, но проблема в том, что между всеми 30 таблицами, которые только что описаны, существует гораздо больше отношений [Client-City, Client-Agency, Agency -City], поэтому окончательная процедура, связанная с кэшированием всех объектов, скорее всего, ударит по запасам устройств iPad для моего приложения.


ОБНОВЛЕНИЕ 4

Сообщение для будущих респондентов: я изо всех сил старался сделать этот ответ достаточно подробным и хорошо сформированным, и я действительно ожидаю, что вы ответите на подробные ответы. Было бы здорово, если бы ваш ответ действительно рассмотрел сложность проблемы, обсуждаемой здесь, и дополнял мои усилия, которые я сделал, чтобы сделать мой вопрос ясным и общим как можно больше. Благодарю.

ОБНОВЛЕНИЕ 5

Связанные темы: Основные данные на клиенте (iOS) для кэширования данных из стратегии сервера, Попытка сделать POST запросить с RestKit и отобразить ответ на основные данные.

ОБНОВЛЕНИЕ 6

Даже после того, как больше не возможно открыть новые щедрости, и есть принятый ответ, я по-прежнему буду рад увидеть любые другие ответы, содержащие дополнительную информацию о проблеме, которая адресована этим темам. Спасибо заранее.

Ответы

Ответ 1

Я решил написать свой собственный ответ, обобщая методы и советы, которые я нашел полезными для своей ситуации. Спасибо всем, кто отправил свои ответы.


I. Транспорт

  • "Один JSON". Это идея, которую я хочу попробовать. Спасибо @mundi.

  • Идея архивирования JSON перед отправкой его клиенту, будь то один пакет JSON или 30 отдельных "одна таблица - один пакет".


II. Настройка отношений основных данных

Я опишу процесс импорта импорта JSON- > CoreData с использованием воображаемой операции большого импорта, как если бы он выполнялся одним способом (я не уверен, будет это так или нет - возможно, я разделил его на логические куски).

Предположим, что в моем воображаемом приложении есть 15 емких таблиц, где "емкий" означает "нельзя держать в памяти сразу, следует импортировать с помощью партий" и 15 не-емких таблиц, каждый из которых имеет 500 записей для пример:

Вместительная:

  • города (15k +)
  • клиенты (30k +)
  • пользователи (15k +)
  • события (5k +)
  • действия (2k +) ...

Маленький:

  • client_types (20 -)
  • visit_types (10 -)
  • позиции (10-) ...

Представьте себе, что у меня уже есть пакеты JSON, загруженные и проанализированные в составные переменные NSArray/NSDictionary: у меня есть городаJSON, клиентыJSON, usersJSON,...

1. Сначала работайте с маленькими таблицами

Мой псевдо-метод начинается с импорта крошечных таблиц. Возьмем таблицу client_types: итерацию через clientTypesJSON и создание объектов ClientType (подклассы NSManagedObject). Более того, я собираю результирующие объекты в словаре с этими объектами как свои значения и "идентификаторы" (внешние ключи) этих объектов в качестве ключей.

Вот псевдокод:

NSMutableDictionary *clientTypesIdsAndClientTypes = [NSMutableDictionary dictionary];
for (NSDictionary *clientTypeJSON in clientsJSON) {
    ClientType *clientType = [NSEntityDescription insertNewObjectForEntityForName:@"ClientType" inManagedObjectContext:managedObjectContext];

    // fill the properties of clientType from clientTypeJSON

    // Write prepared clientType to a cache
    [clientTypesIdsAndClientTypes setValue:clientType forKey:clientType.id];
}

// Persist all clientTypes to a store.
NSArray *clientTypes = [clientTypesIdsAndClientTypes allValues];
[managedObjectContext obtainPermanentIDsForObjects:clientTypes error:...];

// Un-fault (unload from RAM) all the records in the cache - because we don't need them in memory anymore.
for (ClientType *clientType in clientTypes) {
    [managedObjectContext refreshObject:clientType mergeChanges:NO];
}

В результате у нас есть куча словарей небольших таблиц, каждая из которых имеет соответствующий набор объектов и их идентификаторы. Мы будем использовать их позже без повторной проверки, потому что они маленькие, а их значения (NSManagedObjects) теперь являются ошибками.

2. Используйте словарь кеша объектов из небольших таблиц, полученных на шаге 1, чтобы установить с ними отношения

Рассмотрим комплексную таблицу clients: мы имеем clientsJSON, и нам нужно настроить отношение ClientType для каждой записи клиента, это легко, потому что у нас есть кеш с clientTypes и их идентификаторы:

for (NSDictionary *clientJSON in clientsJSON) {
    Client *client = [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext];

    // Setting up SQLite field 
    client.client_type_id = clientJSON[@"client_type_id"];

    // Setting up Core Data relationship beetween client and clientType
    client.clientType = clientTypesIdsAndClientTypes[client.client_type_id];
}

// Save and persist

3. Работа с большими таблицами - партии

Рассмотрим большой clientsJSON, имеющий в нем 30k + клиентов. Мы не перебираем все clientsJSON, а разбиваем на куски соответствующего размера (500 записей), так что [managedObjectContext save:...] вызывается каждые 500 записей. Также важно обернуть операцию с каждой записью 500 записей в @autoreleasepool block - см. Сокращение издержек памяти в руководстве по производительности основных данных

Будьте осторожны - шаг 4 описывает операцию, применяемую к пакету 500 записей, а не целому числу clientsJSON!

4. Работа с большими таблицами - настройка отношений с большими таблицами

Рассмотрим следующий метод, который мы будем использовать в один момент:

@implementation NSManagedObject (Extensions)
+ (NSDictionary *)dictionaryOfExistingObjectsByIds:(NSArray *)objectIds inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
    NSDictionary *dictionaryOfObjects;

    NSArray *sortedObjectIds = [objectIds sortedArrayUsingSelector:@selector(compare:)];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass(self)];

    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(id IN %@)", sortedObjectIds];
    fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey: @"id" ascending:YES]];

    fetchRequest.includesPropertyValues = NO;
    fetchRequest.returnsObjectsAsFaults = YES;

    NSError *error;
    NSArray *fetchResult = [managedObjectContext executeFetchRequest:fetchRequest error:&error];

    dictionaryOfObjects = [NSMutableDictionary dictionaryWithObjects:fetchResult forKeys:sortedObjectIds];

    return dictionaryOfObjects;
}
@end

Рассмотрим пакет clientsJSON, содержащий партию (500) записей Client, которые нам нужно сохранить. Также нам нужно установить связь между этими клиентами и их агентствами (Agency, внешний ключ agency_id).

NSMutableArray *agenciesIds = [NSMutableArray array];
NSMutableArray *clients = [NSMutableArray array];

for (NSDictionary *clientJSON in clientsJSON) {
    Client *client = [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext];

    // fill client fields...

    // Also collect agencies ids
    if ([agenciesIds containsObject:client.agency_id] == NO) {
        [agenciesIds addObject:client.agency_id];
    }        

    [clients addObject:client];
}

NSDictionary *agenciesIdsAndAgenciesObjects = [Agency dictionaryOfExistingObjectsByIds:agenciesIds];

// Setting up Core Data relationship beetween Client and Agency
for (Client *client in clients) {
    client.agency = agenciesIdsAndAgenciesObjects[client.agency_id];
}

// Persist all Clients to a store.
[managedObjectContext obtainPermanentIDsForObjects:clients error:...];

// Un-fault all the records in the cache - because we don't need them in memory anymore.
for (Client *client in clients) {
    [managedObjectContext refreshObject:client mergeChanges:NO];
}

Большая часть того, что я здесь использую, описана в этих руководствах Apple: Производительность основных данных, Эффективный импорт данных. Итак, сводка для шагов 1-4 следующая:

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

  • Создайте словари с объектами как значениями и их ids как ключи, поэтому эти словари могут служить в качестве поисковых таблиц при построении отношений между этими объектами и другими объектами.

  • Используйте @autoreleasepool при повторении через большое количество записей.

  • Используйте метод, похожий на dictionaryOfExistingObjectsByIds, или метод, который Том ссылается в своем ответе, Эффективно импортировать данные - a метод, который имеет SQL IN предикат за ним, чтобы значительно сократить количество выборок. Прочтите ответ Tom и обратитесь к соответствующему руководству Apple, чтобы лучше понять эту технику.


Хорошее чтение по этой теме

ошибка objc.io # 4: импорт больших наборов данных

Ответ 2

У меня есть опыт в очень похожем проекте. Вставки Core Data занимают некоторое время, поэтому мы обязываем пользователя, что это займет некоторое время, но только в первый раз. Лучшая настройка производительности была, конечно, для того, чтобы получить размер партии прямо между сэйвами, но я уверен, что вы это знаете.

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

Вместо этого я обнаружил, что загрузка всего JSON за один раз была намного быстрее. Я не знаю, сколько данных у вас есть, но я тестировал s > 100 000 записей и 40 МБ + JSON-строку, это работает очень быстро, поэтому узким местом являются только вставки Core Data. С пулом @autorelease это даже выполнило приемлемо на iPad первого поколения.

Держитесь подальше от SQLite API - вам потребуется больше года (при высокой производительности), чтобы реплицировать оптимизацию производительности, которую вы получаете из коробки с помощью Core Data.

Ответ 3

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

Я бы рекомендовал делать ваши выборки партиями, с размером партии, соответствующим размеру партии для обработки новых объектов. Например, при создании новых записей Agency выполните следующие действия:

  • Убедитесь, что текущая партия Agency сортируется по city_id. (Я объясню, почему позже).

  • Получите идентификатор City для каждого Agency в пакете. В зависимости от того, как структурирован ваш JSON, это, вероятно, однострочный (так как valueForKey работает с массивами):

    NSArray *cityIDs = [myAgencyBatch valueForKey:@"city_id"];
    
  • Получите все экземпляры City для текущего прохода в одной выборке, используя идентификаторы, найденные вами на предыдущем шаге. Сортировка результатов city_id. Что-то вроде:

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"City"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"city_id in %@", cityIDs];
    [request setPredicate:predicate];
    [request setSortDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"city_id" ascending:YES] ]];
    NSArray *cities = [context executeFetchRequest:request error:nil];
    

Теперь, у вас есть один массив Agency и еще один из City, отсортированный по city_id. Сопоставьте их, чтобы настроить отношения (проверьте city_id, если все не соответствует). Сохраните изменения и перейдите к следующей партии.

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

Еще одна вещь, которая может помочь, - "разогреть" внутренний кэш Core Data с помощью объектов, которые вам нужны, прежде чем вы их начнете. Это позволит сэкономить время позже, потому что получение значений свойств не потребует поездки в хранилище данных. Для этого вы бы сделали что-то вроде:

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"City"];
// no predicate, get everything
[request setResultType:NSManagedObjectIDResultType];
NSArray *notUsed = [context executeFetchRequest:request error:nil];

.., а затем просто забудьте о результатах. Это поверхностно бесполезно, но изменит внутреннее состояние Core Data для более быстрого доступа к экземплярам City позже.

Теперь, что касается ваших других вопросов,

  • Использование SQLite напрямую вместо Core Data не может быть ужасным выбором для вашей ситуации. Выгода будет заключаться в том, что вам не нужно будет устанавливать отношения, поскольку вы можете использовать поля использования, такие как city_id, как внешние ключи. Итак, быстрый импорт. Недостатком, конечно же, является то, что вам придется выполнять свою собственную работу по преобразованию объектов модели в/из записей SQL и, возможно, переписать довольно много существующего кода, который предполагает Core Data (например, каждый раз, когда вы будете следовать отношениям, которые вы сейчас нужно искать записи по этому внешнему ключу). Это изменение может устранить проблемы с производительностью импорта, но побочные эффекты могут быть значительными.

  • JSON, как правило, очень хороший формат, если вы передаете данные как текст. Если вы можете подготовить хранилище основных данных на сервере и , если, вы должны использовать этот файл как есть вместо того, чтобы пытаться объединить его в существующее хранилище данных, тогда почти наверняка ускорит ситуацию. Ваш процесс импорта будет запускаться один раз на сервере, а затем никогда больше. Но это большие "если", особенно второй. Если вы доберетесь до того, где вам нужно объединить новое хранилище данных сервера с существующими данными, вы вернетесь туда, где вы сейчас находитесь.

Ответ 4

У вас есть контроль над сервером? Я спрашиваю, потому что это звучит так, как из следующего абзаца:

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

Если отправка дампа возможна, почему бы не отправить файл Core Data непосредственно? Основные данные (по умолчанию) поддерживаются базой данных SQLite - почему бы не создать эту базу данных на сервере, не закрепить ее и не отправить через провод?

Это означает, что вы можете устранить все синтаксические разборки JSON, сетевые запросы и т.д. и заменить его простой загрузкой файлов и извлечением архива. Мы сделали это по проекту, и он неизмеримо улучшил производительность.

Ответ 5

  • Для каждой строки в таблице должен быть столбец timestamp. Если его нет, вы должны добавить его.
  • В первый раз и каждый раз, когда вы извлекаете дамп базы данных, вы сохраняете последнюю дату и время обновления.
  • В следующий раз вы укажете базе данных, чтобы вернуть только те записи, которые были изменены или обновлены со времени предыдущей операции загрузки. Для удаления исчезнувших записей также должен быть флаг "deleted".
  • Тогда вам нужно только обновить некоторые соответствующие записи, экономя время на всех фронтах.

Чтобы ускорить синхронизацию в первый раз, вы также можете отправить семенную базу данных с помощью приложения, чтобы ее можно было сразу импортировать без каких-либо сетевых операций.

  • Загрузите файлы JSON вручную.
  • Поместите их в свой проект.
  • Где-то в конфигурации проекта или файлах заголовка обратите внимание на дату и время загрузки.
  • В первом запуске найдите и загрузите указанные файлы, затем продолжайте, как вы их обновляете.
  • Если есть сомнения, обратитесь к руководству.

Пример:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"cities" 
                                            ofType:@"json"];
NSData *citiesData = [NSData dataWithContentsOfFile:filePath];
// I assume that you're loading an array
NSArray *citiesSeed = [NSJSONSerialization JSONObjectWithData:citiesData 
                       options:NSJSONReadingMutableContainers error:nil];

Ответ 6

Здесь у вас есть мои рекомендации:

  • Используйте magicalrecord. Это оболочка CoreData, которая сэкономит вам много шаблонов кода, плюс она поставляется с очень интересными функциями.
  • Загрузите все JSON по одному запросу, как и другие. Если вы можете вставить первый JSON-документ в приложение, вы можете сохранить время загрузки и начать заполнять базу данных сразу при первом открытии приложения. Кроме того, с magicalrecord довольно легко выполнить эту операцию сохранения в отдельном потоке, а затем автоматически синхронизировать все контексты. Это может улучшить оперативность вашего приложения.
  • Кажется, что вам нужно реорганизовать этот уродливый метод, как только вы решите первую проблему импорта. Опять же, я бы предложил использовать magicalrecord для легкого создания этих объектов.

Ответ 7

Недавно мы переместили довольно большой проект из Core Data в SQLite, и одной из основных причин была массовая вставка. Было много функций, которые мы потеряли при переходе, и я бы не советовал вам переключаться, если вы можете избежать этого. После перехода на SQLite у нас действительно были проблемы с производительностью в областях, отличных от массовых вставок, которые Core Data прозрачно обрабатывали для нас, и хотя мы исправили эти новые проблемы, потребовалось некоторое время, чтобы вернуться и работать. Хотя мы потратили некоторое время и усилия на переход от Core Data к SQLite, я не могу сказать, что есть какие-либо сожаления.

С учетом этого, я бы посоветовал вам получить некоторые базовые измерения, прежде чем приступать к исправлению производительности объемной вставки.

  • Измерьте, сколько времени потребуется, чтобы вставить эти записи в текущее состояние.
  • Пропустите настройку отношений между этими объектами в целом, а затем измерьте производительность вставки.
  • Создайте простую базу данных SQLite и оцените ее производительность. Это должно дать вам очень хорошую базовую оценку того, сколько времени потребуется для выполнения фактических вставок SQL, а также даст вам хорошее представление о накладных расходах Core Data.

Несколько вещей, которые вы можете попробовать с места в карьер для ускорения вставок:

  • Убедитесь, что нет активных выбранных контроллеров результатов при выполнении объемных вставок. По активным я имею в виду получаемые контроллеры результатов, у которых есть делегат, отличный от нуля. По моему опыту, отслеживание изменений Core Data было самой дорогостоящей операцией при попытке делать массовые вставки.
  • Выполнять все изменения в одном контексте и прекращать слияние изменений из разных контекстов до тех пор, пока эти объемные вставки не будут выполнены.

Чтобы получить более полное представление о том, что действительно происходит под капотом, включите Отладку основных данных SQL и просмотрите выполняемые SQL-запросы. В идеале вы хотите увидеть много INSERT и несколько UPDATE. Но если вы встретите слишком много SELECT и/или UPDATE, то это знак того, что вы слишком много читаете или обновляете объекты.

Используйте инструмент профилировщика Core-Data, чтобы получить более качественный обзор того, что происходит с Core Data.