Arhn - архитектура программирования

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

У меня есть серьезные проблемы с производительностью при запросе базы данных sqlite с прибл. 25к строк. Я хочу сделать следующее: когда пользователь вводит что-то в текстовое поле, я хочу дать ему предложения автозаполнения в табличном представлении, которое является inputAccessoryView клавиатуры. Каждый раз, когда он вводит новый символ, отправляются 4 новых запроса для поиска подходящих предложений. Я делаю это в отдельном потоке, используя GCD и блоки. Однако производительность слишком низкая. Вот код одного запроса:

- (void) queryDatabase:(NSString *) searchString
{   
[self.fetchedResults removeAllObjects];

dispatch_queue_t fetchQueue = dispatch_queue_create("Fetch Queue", NULL);

dispatch_async(fetchQueue,^{
    NSError *error = nil;
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    context.undoManager = nil;
    [context setPersistentStoreCoordinator: self.persistentStoreCoordinator];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"reducedTownName like %@", [searchString stringByAppendingString:@"*"]];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = [NSEntityDescription entityForName:@"Station" inManagedObjectContext:context];
    request.fetchLimit = 20;
    [request setIncludesPropertyValues:NO];
    request.predicate = predicate;
    request.resultType = NSManagedObjectIDResultType;

    NSArray *results = [context executeFetchRequest:request error:&error];
    NSEnumerator *e = [results objectEnumerator];
    NSManagedObjectID *objectId = nil;

    while (objectId = [e nextObject]) 
    {
        Station *station = (Station *) [self.managedObjectContext objectWithID:objectId];
        if ( ![self.fetchedResults containsObject:station])
            [self.fetchedResults addObject:station];
    }       

    dispatch_async(dispatch_get_main_queue(),^{
        [self.tableView reloadData];
    });
    [request release];
    [context release];
});
    //do 3 more queries similar to the first (only predicate changes)
    dispatch_release(fetchQueue);
}

Я использую NSArray (fetchedResults) для хранения возвращенных сущностей и обновления tableView данными из этого массива.

Видит ли кто-нибудь убийцу производительности в этом коде или может дать мне другой совет?


  • Какова производительность (в миллисекундах на запрос)? 21.12.2010
  • Я заменил LIKE на BEGINSWITH, и теперь время запроса варьируется от мгновенного до примерно 400 мс, что в основном приемлемо. В любом случае, я открыт для предложений по дальнейшему увеличению производительности. 22.12.2010

Ответы:


1

Думаю, я мог это заметить.

Вы делаете свой запрос, чтобы получить сущности и поместить их в массив результатов. Это запрос номер 1, и он оптимизирован нормально (при условии, что соединений нет и т. д., но не похоже, что они есть)

Затем вы просматриваете все результаты, чтобы удалить дубликаты, которые уже есть в ваших результатах. Это означает, что вы выполняете новый запрос для получения каждой станции NSManagedObject. Если вы хотите избежать дубликатов, вам придется сделать это без вызова objectWithID, потому что это приведет к получению объекта из CoreData.

В худшем случае, чтобы получить результаты 20 станций, вам потребуется 21 запрос. Фигово.

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

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

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

Я также могу использовать NSFetchedResultsController. объединить мои результаты.

Это превратилось в небольшой бред - если у вас есть какие-либо вопросы, просто задавайте!

Надеюсь, поможет,

Сэм

21.12.2010
  • Хм, я получаю идентификатор объекта только во вторичном потоке и передаю его в контекст основного управляемого объекта, чтобы объединить результаты. Это один из способов, которым Apple описывает это. Или я неправильно вас понял? В любом случае, я заменил LIKE на BEGINSWITH, и производительность резко увеличилась. Как только я включаю дескрипторы сортировки, производительность падает, вероятно, потому, что она будет извлекать все записи, сортировать их и возвращать только двадцать. Есть ли способ построить базу данных таким образом, чтобы запрос автоматически возвращал первые N результатов в лексикографическом порядке без использования дескрипторов сортировки? 22.12.2010
  • Ты? Если вы установите точку останова на строке Station *station = (Station *) [self.managedObjectContext objectWithID:objectId];, в каком потоке она будет работать? 22.12.2010
  • там написано Thread-3-‹Fetch Queue›. это точно не основная тема. 22.12.2010
  • Да, вы правы, и если вы передаете идентификаторы, это определенно правильный способ сделать это :) 22.12.2010

  • 2

    Хорошо, я наконец понял. Заменил запрос LIKE на

    reducedTownName >= %@ AND reducedTownName <= %@
    

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

    reducedTownName >= 'oak' AND reducedTownName <= 'oal'
    

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

    Привет

    Сэм

    28.12.2010
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..