Проблемы производительности с Mongo + PHP с нумерацией страниц, различные значения - PullRequest
3 голосов
/ 30 августа 2011

У меня есть коллекция mongodb, содержащая множество книг со многими полями.Вот некоторые ключевые поля, которые имеют отношение к моему вопросу:

{
book_id : 1, 
book_title :"Hackers & Painters", 
category_id : "12",
related_topics : [ {topic_id : "8", topic_name : "Computers"},
                   {topic_id : "11", topic_name : "IT"}
                 ]
...
... (at least 20 fields more)
...
}

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

Мы стараемся использовать MongoDB вместо PostgreSQL.Потому что производительность и скорость - наша главная забота об этом процессе.

Теперь вопрос в следующем:

Я могу легко отфильтровать результаты, используя функцию "найти" со всеми параметрами фильтра.Это круто.Я могу разбить на страницы результаты с помощью функций пропуска и ограничения:

$data = $lib_collection->find($filter_params, array())->skip(20)->limit(20);

Но мне нужно собрать количество результатов, найденных для каждого category_id и topic_id, прежде чем произойдет разбиение на страницы.И я не хочу «предвидеть» все результаты, собирать категории и управлять разбиением на страницы с помощью PHP, потому что отфильтрованные данные часто содержат почти 200 000 результатов.

Проблема 1: Я обнаружил mongodb:: command () функция в руководстве по PHP с «отличным» примером.Я думаю, что я получаю различные значения этим методом.Но командная функция не принимает условные параметры (для фильтрации).Я не знаю, как применить те же параметры фильтра при запросе различных значений.

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

Задача 3: Чтобы получить разные topic_ids с количеством результатов, будет другой запрос, другое штраф скорости: (*

IЯ новичок в работе с MongoDB. Возможно, я смотрю на проблемы с неправильной точки зрения. Можете ли вы помочь мне решить проблемы и высказать свое мнение о самом быстром способе получения:

  • отфильтрованных результатов
  • нумерация страниц
  • отдельные значения с количеством найденных результатов

из большого набора данных.

Ответы [ 2 ]

3 голосов
/ 08 сентября 2011

Разбивка

Будьте осторожны с разбиением на страницы на больших наборах данных. Помните, что skip() и take() - не имеет значения, используете ли вы индекс или нет - придется выполнить сканирование. Поэтому пропуск очень далеко очень медленный.

Подумайте об этом так: в базе данных есть индекс (B-Tree), который может сравнивать значения друг с другом: он может быстро сказать вам, является ли что-то большим или меньшим, чем заданное x. Следовательно, времена поиска в хорошо сбалансированных деревьях являются логарифмическими. Это не верно для индексации на основе счетчика: B-дерево не может быстро сказать, что такое 15.000-й элемент: ему придется пройтись и перечислить все дерево.

Из документации :

Пейджинговые расходы

К сожалению, пропуск может быть (очень) дорогостоящим и требует сервер, чтобы идти от начала коллекции, или индекса, чтобы получить в положение смещения / пропуска, прежде чем он сможет начать возвращать страницу данные (предел). По мере увеличения номера страницы пропуск станет медленнее и более интенсивный процессор, и, возможно, связанный с IO, с большими коллекциями.

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

Убедитесь, что вам действительно нужна эта функция: как правило, никто не заботится о 42436-м результате. Обратите внимание, что большинство крупных сайтов никогда не позволяют разбивать страницы на страницы, не говоря уже о точных итогах. На эту тему есть отличный сайт, но у меня нет ни адреса, ни названия, чтобы найти его.

Количество разделенных тем

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

Если ваши документы очень большие, производительность является проблемой, и вы ненавидите ORM так же сильно, как и я, вы можете рассмотреть возможность использования и MongoDB, и выбранной вами СУБД: пусть MongoDB извлекает результаты и СУБД агрегирует лучшие совпадения для данной категории. Вы даже можете выполнять запросы параллельно! Конечно, запись изменений в БД должна происходить в обеих базах данных.

3 голосов
/ 30 августа 2011

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

$cursor = $lib_collection->find($filter_params, array())
$count = $cursor->count();
$data = $cursor->skip(20)->limit(20);

Однако этот метод не может быть несколько неэффективным. Если вы запрашиваете поля, которые не проиндексированы, сервер может считать «count ()» единственным способом - загрузить каждый документ и проверить. Если вы делаете skip() и limit() без sort(), то серверу просто нужно найти первые 20 подходящих документов, что намного меньше работы.

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

Если данные меняются не часто, вы можете предварительно рассчитать эти значения, используя обычные карты / сокращенные задания. В противном случае вы должны выполнить серию distinct() команд или встроенную карту / уменьшить. Ни один из них, как правило, не предназначен для специальных запросов.

Единственный другой вариант - загрузить все результаты поиска, а затем рассчитывать на веб-сервер (вместо БД). Очевидно, что это также неэффективно.

Получение всех этих функций потребует некоторого планирования и компромиссов.

...