Базовая производительность данных

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

Введение

Базовые Данные являются богатой и сложной платформой управления графом объектов, способной к контакту с большими объемами данных. Хранилище SQLite может масштабироваться к измеренным базам данных терабайта с миллиардами строк/таблиц/столбцов. Если Ваши объекты сами не имеют очень большие атрибуты (несмотря на то, что видят Большие Объекты данных (BLOBs)), или большие количества свойств, 10 000 объектов считается довольно небольшим размером для набора данных.

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

NSManagedObject использует механизм внутренней памяти для высоко оптимизированных данных. В частности это эффективно использует информацию о типах данных, которые доступны посредством анализирования модели. Когда Вы храните и получаете данные способом, который является кодированием значения ключа и значением ключа, наблюдающим совместимый, это вероятно то использование NSManagedObject будет быстрее, чем какой-либо другой механизм хранения — включая для простого получает/устанавливает случаи. В современном приложении Какао, эффективно использующем Привязку Какао, учитывая что Привязка Какао уверена после кодирования значения ключа и значения ключа, наблюдая его, было бы трудным создать механизм хранения необработанных данных, обеспечивающий тот же уровень эффективности как Базовые Данные.

Как все технологии, однако, можно злоупотребить Базовыми Данными. Используя Базовые Данные не освобождает Вас от потребности рассмотреть основные образцы Какао, такие как управление памятью. Необходимо также рассмотреть, как Вы выбираете данные от персистентного хранилища. Если Вы находите, что Ваше приложение не выполняет, а также Вы хотели бы, необходимо использовать профильные инструменты, такие как Инструменты для определения, где проблема заключается (см. Производительность).

Выборка управляемых объектов

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

Предикаты выборки

То, как Вы используете предикаты, может значительно влиять на производительность Вашего приложения. Если запрос выборки требует составного предиката, можно сделать выборку более эффективной путем обеспечения, что самый строгий предикат является первым, особенно если предикат включает текст, соответствующий (contains, endsWith, like, и matches) так как корректный поиск Unicode является медленным. Если предикат объединит текстовые и нетекстовые сравнения, то, вероятно, будет более эффективно указать нетекстовые предикаты сначала, например (salary > 5000000) AND (lastName LIKE 'Quincey') лучше, чем (lastName LIKE 'Quincey') AND (salary > 5000000). Для больше о создании предикатов, см. Руководство по программированию Предиката.

Пределы выборки

Можно установить предел к числу объектов, выборка возвратит использование метода setFetchLimit: как показано в следующем примере.

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setFetchLimit:100];

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

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

До OS X v10.6, нет никакого способа “обработать выборки в пакетном режиме” (или в условиях базы данных, установить курсор). Т.е. Вы не можете выбрать «первые» 100 объектов, тогда вторые 100, тогда третье, и т.д. В OS X v10.6 и позже и на iOS, можно использовать fetchOffset управлять поддиапазоном произвольного набора результатов.

В целом, однако, Вы призваны использовать предикаты, чтобы гарантировать, чтобы Вы получили только те объекты, которых Вы требуете.

Дающее сбой поведение

Увольнение отказов может быть сравнительно дорогим процессом (потенциально требующий цикла обработки к персистентному хранилищу), и можно хотеть избежать излишне запускать отказ. Можно безопасно вызвать следующие методы на отказ, не заставляя его стрелять: isEqual:, hash, superclass, class, self, zone, isProxy, isKindOfClass:, isMemberOfClass:, conformsToProtocol:, respondsToSelector:, description, managedObjectContext, entity, objectID, isInserted, isUpdated, isDeleted, и isFault.

С тех пор isEqual и hash не заставляйте отказ стрелять, управляемые объекты могут обычно помещаться в наборы, не запуская отказ. Отметьте, однако, что вызов методов кодирования значения ключа на объекте коллекции мог бы поочередно привести к вызову valueForKey: на управляемом объекте, который запустил бы отказ. Кроме того, несмотря на то, что реализация по умолчанию description если Вы реализуете пользовательское, не заставляет отказ стрелять description метод, получающий доступ к персистентным свойствам объекта, это заставит отказ стрелять.

Обратите внимание на то, что просто, потому что управляемый объект является отказом, это не обязательно означает, что данные для объекта не находятся в памяти — см. определение для isFault.

Пакетный сбой и упреждающая выборка с хранилищем SQLite

При выполнении выборки Базовых выборок Данных просто экземпляры объекта, Вы указываете. В некоторых ситуациях (см. Дающие сбой Пределы Размер Графа объектов), место назначения отношения представлено отказом. Базовые Данные автоматически решают (запускает) отказ, когда Вы получаете доступ к данным в отказе. Эта ленивая загрузка связанных объектов намного лучше для использования памяти, и намного быстрее для выборки объектов, связанных с редко используемым (или очень большой) объекты. Это может также, однако, привести к ситуации, где Базовые Данные выполняют отдельные запросы выборки на многие отдельные объекты, который подвергается сравнительно высоким издержкам. Например, учитывая модель:

../Art/relationship_cardinality_2x.png

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

NSFetchRequest * employeesFetch = <#A fetch request for Employees#>
// The request should include a predicate -- if you don't have a predicate here,
// you should probably just fetch all the Departments.
NSArray *fetchedEmployees = [moc executeFetchRequest:employeesFetch error:&error];
for (Employee *employee in fetchedEmployees)
{
    NSLog(@"%@ -> %@ department", employee.name, employee.department.name);
}

Это могло бы привести к следующему поведению:

Jack -> Sales [fault fires]
Jill -> Marketing [fault fires]
Benjy -> Sales
Gillian -> Sales
Hector -> Engineering [fault fires]
Michelle -> Marketing

Здесь, существует четыре цикла обработки к персистентному хранилищу (один для исходной выборки Сотрудников, и три для отдельных Отделов), который представляет значительные издержки поверх минимума (два — один для каждого объекта).

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

Пакетный сбой

Можно обработать отказ в пакетном режиме набор объектов путем выполнения запроса выборки с помощью предиката с IN оператор, как проиллюстрировано следующим примером. (В предикате, self представляет оцениваемый объект — посмотрите Синтаксис Строки формата Предиката.)

NSArray *array = [NSArray arrayWithObjects:fault1, fault2, ..., nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@", array];

В OS X v10.5 и позже при создании запроса выборки можно использовать NSFetchRequest метод setReturnsObjectsAsFaults: гарантировать, что управляемые объекты не возвращаются как отказы.

Упреждающая выборка

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

В OS X v10.5 и позже, можно использовать NSFetchRequest метод setRelationshipKeyPathsForPrefetching: указать массив ключа отношения соединяет каналом для упреждающей выборки вместе с объектом для запроса. Например, учитывая объект Сотрудника с отношением к объекту Отдела: если Вы выбираете всех сотрудников тогда для каждого, распечатывают их имя и имя отдела, которому они принадлежат, можно избежать возможности отказа, увольняемого за каждый экземпляр Отдела путем упреждающей выборки отношения отдела, как проиллюстрировано в следующем фрагменте кода:

NSManagedObjectContext *context = /* get the context */;
NSEntityDescription *employeeEntity = [NSEntityDescription
    entityForName:@"Employee" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:employeeEntity];
[request setRelationshipKeyPathsForPrefetching:
    [NSArray arrayWithObject:@"department"]];

В OS X v10.4, Вы создаете запрос выборки для выборки просто тех экземпляров целевого объекта, связанных с исходными объектами, которые Вы просто получили, это сокращает количество выборок к два (минимум). То, как (или ли) Вы реализуете упреждающую выборку, зависит от кардинальности отношения.

  • Если обратная связь к - один, можно использовать предикат с форматом, @"%K IN %@" где первым параметром является ключевое имя для обратной связи и второй параметр массив исходных объектов.

  • Если обратная связь к - многие, Вы сначала собираете идентификаторы объектов из отказов, о которых Вы заботитесь о (быть тщательным не касаются других атрибутов). Вы тогда создаете предикат с форматом, @"SELF IN %@", где параметром является массив идентификаторов объектов.

  • Если отношение является many-many, упреждающая выборка не рекомендуется.

Вы могли реализовать упреждающую выборку для отношения отдела в предыдущем примере следующим образом.

NSEntityDescription *deptEntity = [NSEntityDescription entityForName:@"Department"
        inManagedObjectContext:moc];
NSArray *deptOIDs = [fetchedEmployees valueForKeyPath:@"department.objectID"];
NSPredicate *deptsPredicate = [NSPredicate predicateWithFormat:
        @"SELF in %@", deptOIDs];
NSFetchRequest *deptFetch = [[NSFetchRequest alloc] init];
[deptFetch setEntity:deptEntity];
[deptFetch setPredicate:deptsPredicate];
// Execute the fetch.

Если Вы знаете что-то о том, как к данным получат доступ или представят, можно далее совершенствовать предикат выборки для сокращения количества выбранных объектов. Отметьте, тем не менее, что этот метод может быть хрупким — если для изменений приложений и нужен различный набор данных, то можно закончить тем, что выбрали неправильные объекты с упреждением.

Для больше о сбое, и в частности значение значения возвратилось из isFault, посмотрите Faulting и Uniquing.

Сокращение памяти наверху

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

Большие объекты данных (BLOBs)

Если Ваше приложение использует большой BLOBs («Большие двоичные объекты», такие как изображение и звуковые данные), необходимо заботиться для минимизации издержек. Точное определение «маленьких», «умеренных», и «большой» жидко и зависит от использования приложения. Свободное эмпирическое правило - то, что объекты в порядке килобайтов в размере имеют измеренное «умеренное», и те в порядке мегабайтов в размере «большие» измеренный. Некоторые разработчики достигли хорошей производительности с 10 МБ BLOBs в базе данных. С другой стороны, если приложение имеет миллионы строк в таблице, даже 128 байтами мог бы быть «умеренный» размерный CLOB (Символьный Большой объект), который должен быть нормализован в отдельную таблицу.

В целом, если необходимо сохранить BLOBs в персистентном хранилище, необходимо использовать хранилище SQLite. XML и двоичные хранилища требуют, чтобы целый граф объектов находился в памяти и сохранил записи, являются атомарными (см. Персистентные Функции Хранилища), что означает, что они эффективно не имеют дело с большими объектами данных. SQLite может масштабироваться для обработки чрезвычайно больших баз данных. Должным образом используемый, SQLite обеспечивает хорошую производительность для баз данных до 100 ГБ, и единственная строка может содержать до 1 ГБ (несмотря на то, что, конечно, чтение 1 ГБ данных в память является дорогой работой независимо от того как эффективный репозиторий).

BLOB часто представляет атрибут объекта — например, фотография могла бы быть атрибутом объекта Сотрудника. Для маленького к умеренному размерному BLOBs (и CLOBs), необходимо создать отдельный объект для данных и создать к - одно отношение вместо атрибута. Например, Вы могли бы создать объекты Сотрудника и Фотографии с непосредственным отношением между ними, где отношение от Сотрудника к Фотографии заменяет атрибут фотографии Сотрудника. Этот образец максимизирует преимущества сбоя объекта (см. Faulting и Uniquing). Любая данная фотография только получена, если фактически необходимо (если отношение пересечено).

Однако, если Вы в состоянии сохранить BLOBs как ресурсы в файловой системе и поддержать ссылки (такие как URLs или пути) к тем ресурсам, лучше. Можно тогда загрузить BLOB как и, когда необходимо.

Анализ производительности

Анализ поведения выборки с SQLite

С версией 10.4.3 OS X и позже, можно использовать пользовательское значение по умолчанию com.apple.CoreData.SQLDebug зарегистрировать к stderr фактический SQL отправил к SQLite. (Обратите внимание на то, что пользовательские имена по умолчанию чувствительны к регистру.), Например, можно передать следующий как параметр приложению:

-com.apple.CoreData.SQLDebug 1

Более высокие уровни чисел отладки производят больше информации, несмотря на то, что это, вероятно, будет иметь уменьшение утилиты.

Информация, которую предоставляет вывод, может быть полезной при отладке проблем производительности — в частности это может сказать Вам, когда Базовые Данные выполняют большое количество маленьких выборок (такой, запуская отказы индивидуально). Вывод дифференцируется между выборками, что Вы выполняете использование запроса выборки и выборок, выполняющихся автоматически для понимания отказов.

Инструменты

С версией 10.5 OS X и позже, можно использовать Инструментальное приложение (по умолчанию в/Developer/Applications/) для анализа поведения приложения. Существует несколько Инструментальных зондов, определенных для Базовых Данных:

  • Базовые выборки данных

    Вызовы записей executeFetchRequest:error:, предоставляя информацию об объекте, против которого был выполнен запрос, число объектов возвратилось, и время, потраченное для выборки.

  • Базовые данные сохраняют

    Вызовы записей save: и время, потраченное, чтобы сделать сохранение.

  • Базовые отказы данных

    Информация о записях об объекте и увольнении отказа отношения. Для объектных отказов, записывает даваемый сбой объект; для отказов отношения, записывает исходный объект и запускаемое отношение. В обоих случаях, записывает время, потраченное для увольнения отказа.

  • Базовые неудачные обращения в кэш данных

    Трассировки дают сбой поведение, в частности приводящее к действию файловой системы — указание, что отказ был запущен, для которого никакие данные не были доступны — и записывают время, потраченное для получения данных.

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

При анализе приложения необходимо, конечно, также принять во внимание факторы, не непосредственно связанные с Базовыми Данными, такие как полный объем потребляемой памяти, объектные выделения, использование и злоупотребление другим API, такие как технологии значения ключа и т.д.