Ответ 1
Это поиск иглы в стоге сена. Нам понадобится вывод explain()
для тех запросов, которые не работают хорошо. К сожалению, даже это исправит проблему только для этого конкретного запроса, так что вот стратегия о том, как подойти к этому:
- Обеспечьте это не из-за недостаточной ОЗУ и чрезмерного подкачки
- Включить профилировщик БД (используя
db.setProfilingLevel(1, timeout)
, гдеtimeout
- пороговое значение для количества миллисекунд, которое выполняет запрос или команда, записывается все медленнее) - Осмотрите медленные запросы в
db.system.profile
и запустите запросы вручную, используяexplain()
- Попробуйте определить медленные операции в выводе
explain()
, напримерscanAndOrder
или большойnscanned
и т.д. - Причина выборочности запроса и возможность улучшения запроса с использованием индекса вообще. Если нет, подумайте об отказе в настройке фильтра для конечного пользователя или сообщите ему предупреждение о том, что операция может быть медленной.
Ключевой проблемой является то, что вы, по-видимому, позволяете своим пользователям комбинировать фильтры по своему усмотрению. Без перекрестков индексов, что резко увеличит количество требуемых индексов.
Кроме того, слепо бросать индекс при каждом возможном запросе - очень плохая стратегия. Важно структурировать запросы и убедиться, что индексированные поля имеют достаточную селективность.
Скажем, у вас есть запрос для всех пользователей с status
"active" и некоторыми другими критериями. Но из 5 миллионов пользователей 3 миллиона активных, а 2 миллиона - нет, поэтому более 5 миллионов записей имеют только два разных значения. Такой индекс обычно не помогает. Лучше сначала искать другие критерии, а затем проверять результаты. В среднем при возврате 100 документов вам придется сканировать 167 документов, что не повредит производительности слишком плохо. Но это не так просто. Если основным критерием является дата joined_at
пользователя, и вероятность того, что пользователи прекратят использование со временем, высока, вам может потребоваться сканировать тысячи документов, прежде чем найти сто матчей.
Таким образом, оптимизация во многом зависит от данных (а не только от структуры, но также и от данных), внутренних корреляций и шаблонов запросов.
Вещи ухудшаются, когда данные слишком большие для ОЗУ, потому что тогда, имея индекс большой, но сканирование (или даже просто возвращение), результаты могут потребовать выборку множества данных с диска случайным образом, что занимает много время.
Лучший способ контролировать это - ограничить количество разных типов запросов, запретить запросы на информацию с низкой избирательностью и попытаться предотвратить случайный доступ к старым данным.
Если все остальное терпит неудачу, и если вам действительно нужна такая гибкость в фильтрах, может быть целесообразно рассмотреть отдельную базу данных поиска, которая поддерживает переходы индексов, извлекать идентификаторы mongo, а затем получать результаты от mongo, используя $in
, Но это чревато собственными опасностями.
- EDIT -
Объяснение, которое вы опубликовали, является прекрасным примером проблемы с полями сканирования с низкой селективностью. Видимо, там много документов для "[email protected]". Теперь найти эти документы и отсортировать их по меткам времени очень быстро, потому что он поддерживается индексами высокой избирательности. К сожалению, поскольку существует только два типа устройств, mongo необходимо сканировать 30060 документов, чтобы найти первый, который соответствует "мобильному".
Я предполагаю, что это какое-то веб-отслеживание, а шаблон использования пользователя делает запрос медленным (он будет ежедневно переключаться между мобильным и веб-сайтом, запрос будет быстрым).
Быстрое выполнение этого конкретного запроса может быть выполнено с использованием составного индекса, который содержит тип устройства, например. используя
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})
или
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})
К сожалению, это означает, что запросы типа find({"username" : "foo"}).sort({"timestamp" : -1});
больше не могут использовать тот же самый индекс, поэтому, как описано, количество индексов будет расти очень быстро.
Я боюсь, что в настоящее время нет хорошего решения, использующего mongodb.