Основная масса данных вставки внезапно замедляется до 1/10 скорости - PullRequest
5 голосов
/ 23 августа 2011

Я массово вставляю данные в ядро. У меня есть объект person, и этот объект person имеет отношение, называемое «otherPeople», которое представляет собой NSSet людей. При массовой вставке данных из загрузки все было замечательно, пока не прочитали около 10 000 человек, и в этот момент скорость массовой вставки замедлилась до сканирования. Я сохраняю и сбрасываю свой NSManagedObjectContext каждые 500 вставок.

Если я закомментирую часть, которая вставляет отношения «otherPerson», массовая вставка выполняется быстро в течение всей загрузки. parseJSON вызывается партиями из 500 словарей JSONKit.

Есть идеи, что может быть причиной этого? Возможные решения?

Код:

- (NSArray*) getPeople:(NSArray*)ids
{
    NSFetchRequest* request = [[[NSFetchRequest alloc] init] autorelease];
    NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    [request setEntity:entityDescription];
    [request setFetchBatchSize:ids.count];

    //Filter by array of ids
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"externalId IN %@", ids];
    [request setPredicate:predicate];

    NSError* _error;
    NSArray* people = [context executeFetchRequest:request error:&_error];

    return people;
}

- (void) parseJSON:(NSArray*)people
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSMutableArray* idsToFetch = [NSMutableSet setWithCapacity:CHUNK_SIZE * 3];
    NSMutableDictionary* existingPeople = [NSMutableDictionary dictionaryWithCapacity:CHUNK_SIZE * 3];

    // populate the existing people dictionary first, that way we know who is already in the context without having to do a fetch for each person in the array (externalId IS indexed)
    for (NSDictionary* personDictionary in people)
    {
        // uses JSON kit to parse out all the external ids...
        [PersonJSON addExternalIdsToArray:idsToFetch fromDictionary:personDictionary];
    }

    // see above code for getPeople implementation...
    NSArray* existingPeopleArray = [self getPeople:idsToFetch];
    for (Person* p in existingPeopleArray)
    {
        [existingPeople setObject:p forKey:p.externalId];
    }

    for (NSDictionary* personDictionary in people)
    {
        NSString* externalId = [personDictionary objectForKey:@"PersonId"];
        Person* person = [existingPeople objectForKey:externalId];

        if (person == nil)
        {
            // the person was not in the context, make a new person in the context
            person = [[self newPerson] autorelease];
            person.ancestryId = externalId;
            [existingPeople setObject:person forKey:person.externalId];
        }

        // use JSON kit to populate the core data object...
        [PersonJSON populatePerson:person withDictionary:personDictionary inContext:[self context]];

        // these are just objects that contain an externalId, showing that the link hasn't been setup yet
        for (UnresolvedOtherPerson* other in person.unresolvedOtherPeople)
        {
            Person* relatedPerson = [existingPeople objectForKey:other.externalId];

            if (relatedPerson == nil)
            {
                relatedPerson = [[self newPerson] autorelease];
                relatedPerson.externalId = other.externalId;
                [existingPeople setObject:relatedPerson forKey:relatedPerson.externalId];
            }

            // add link - if I comment out this line, everything runs very fast
            // if I don't comment out, things slow down gradually and then exponentially
            [person addOtherPersonsObject:relatedPerson];
        }

        self.downloaded++;
    }

    [pool drain];
}

Ответы [ 2 ]

3 голосов
/ 22 января 2012

добавление объекта к отношениям приводит к тому, что отношения с обеих сторон сжигаются. Так что, если у вас есть A << - >> B и вы говорите, что пытаетесь добавить только что созданный объект A к объекту B, который уже имеет отношение к 100 000 объектов A, CoreData извлечет эти 100 000 объектов из хранилища, чтобы выполнить это отношение до того, как добавление новых отношений.

Тот факт, что вы периодически очищаете контекст mangedobjectcontext, означает, что все 100 000 компакт-дисков с объектами, загруженными для выполнения отношений, теперь необходимо перезагружать заново, что делает процесс чрезвычайно медленным.

Одним из способов решения этой проблемы является двухэтапный процесс импорта. Сначала загрузите все объекты в БД, не устанавливая никаких отношений, но следите за тем, какие отношения нужно добавить. Как только вы сделаете быстрый импорт, как это, вернитесь к БД и добавьте отношения и очистите контекст таким образом, чтобы избежать необходимости частой перезагрузки этих данных. Так, в качестве конкретного примера, если вам нужно импортировать 1 миллион A, которые должны быть связаны с 100 B, сначала импортируйте все A, а затем для каждой из сотен B загрузите взаимосвязи один раз и добавьте все как к нему, очистите контекст, перейдите к следующему B и так далее. Ключевым моментом здесь является предотвращение сброса контекста тех записей 100 000, которые он только что мучительно загружал.

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

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

EDIT

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

        [self.primitiveTags addObject:tag];

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

0 голосов
/ 26 ноября 2012

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

...