MongoDB — это быстрая база данных NoSQL . К сожалению, это не лекарство от всех ваших проблем с производительностью, и один сложный запрос может привести к остановке вашего кода. Я недавно пережил эту судьбу, и может быть трудно узнать, где искать, когда ваше приложение вдруг становится нестабильным. Я надеюсь, что эти советы помогут вам избежать боли, которую я прошел!
1. Проверьте свой журнал MongoDB
По умолчанию MongoDB записывает все запросы, которые занимают более 100 миллисекунд. Его местоположение определяется в настройке systemLog.path
/var/log/mongodb/mongod.log
Файл журнала может быть большим, поэтому вы можете очистить его перед профилированием. В консоли командной строки mongo введите:
use admin;
db.runCommand({ logRotate : 1 });
Будет запущен новый файл журнала, а старые данные будут доступны в файле с именем и временем резервного копирования. Вы можете удалить резервную копию или переместить ее в другое место для дальнейшего анализа.
Также может быть полезно просмотреть журнал, пока пользователи обращаются к вашей системе. Например:
tail -f /var/log/mongodb/mongod.log
Значения по умолчанию разумны, но вы можете настроить детализацию на уровне журнала или изменить параметры профилирования и изменить время запроса на значение, отличное от 100 миллисекунд. Сначала вы можете установить его на одну секунду, чтобы перехватывать самые неприятные запросы, а затем делить его пополам после каждого набора успешных исправлений.
Ищите строки, содержащие «КОМАНДУ» со временем выполнения в миллисекундах в конце. Например:
2016-02-12T11:05:08.161+0000 I COMMAND
[conn563] command project.$cmd
command: count {
count: "test",
query: { published: { $ne: false },
country: "uk" }
}
planSummary: IXSCAN { country: 1 }
keyUpdates:0
writeConflicts:0
numYields:31
reslen:44
locks: {
Global: {
acquireCount: { r: 64 }
},
MMAPV1Journal: {
acquireCount: { r: 32 }
},
Database: {
acquireCount: { r: 32 }
},
Collection: {
acquireCount: { R: 32 }
}
} 403ms
Это поможет вам определить, где находятся потенциальные узкие места.
2. Анализируйте ваши запросы
Как и многие базы данных, MongoDB предоставляет средство explain
которое показывает, как работает операция с базой данных. Вы можете добавить explain('executionStats')
Например:
db.user.find(
{ country: 'AU', city: 'Melbourne' }
).explain('executionStats');
или добавить его в коллекцию:
db.user.explain('executionStats').find(
{ country: 'AU', city: 'Melbourne' }
);
Это возвращает большой результат JSON, но есть два основных значения, которые нужно изучить:
-
executionStats.nReturned
-
executionStats.totalDocsExamined
Если количество проверенных документов значительно превышает возвращаемое количество, запрос может оказаться неэффективным. В худшем случае MongoDB может сканировать каждый документ в коллекции. Таким образом, запрос выиграет от использования индекса.
Для получения дополнительной информации и примеров обратитесь к разделу Анализ производительности запросов и db.collection.explain () в руководстве MongoDB .
3. Добавить соответствующие индексы
Базы данных NoSQL требуют индексы, как и их родственники. Индекс строится из набора из одного или нескольких полей для быстрого выполнения запросов. Например, вы можете проиндексировать поле country
user
Когда запрос выполняет поиск «AU», MongoDB может найти его в индексе и сослаться на все соответствующие документы, не просматривая всю коллекцию user
Индексы создаются с помощью createIndex
Основная команда для индексации поля country
user
db.user.createIndex({ country: 1 });
Большинство ваших индексов могут быть отдельными полями, но вы также можете создавать составные индексы для двух или более полей. Например:
db.user.createIndex({ country: 1, city: 1 });
Существует много вариантов индексирования, поэтому обратитесь к руководству MongoDB « Введение в индекс» для получения дополнительной информации.
4. Будьте осторожны при сортировке
Вы почти наверняка хотите отсортировать результаты, например, вернуть всех пользователей в порядке возрастания кода страны:
db.user.find().sort({ country: 1 });
Сортировка работает эффективно, когда у вас есть определенный индекс. Подойдет либо отдельный, либо составной индекс, определенный выше.
Если у вас нет определенного индекса, MongoDB должен сам отсортировать результат, и это может быть проблематично при анализе большого набора возвращаемых документов. База данных налагает ограничение на 32 МБ памяти для операций сортировки, и, по моему опыту, 1000 относительно небольших документов достаточно, чтобы перевернуть ее. MongoDB не обязательно вернет ошибку — просто пустой набор записей.
Предел сортировки может быть неожиданным. Предположим, у вас есть индекс по коду country
db.user.createIndex({ country: 1 });
Теперь запрос сортируется по country
city
db.user.find().sort({ country: 1, city: 1 });
В то время как индекс country
city
Это медленно и может превысить предел памяти сортировки 32 МБ. Поэтому вы должны создать составной индекс:
db.user.createIndex({ country: 1, city: 1 });
Операция сортировки теперь полностью проиндексирована и будет выполняться быстро. Вы также можете сортировать в обратном порядке country
city
Например:
db.user.find().sort({ country: -1, city: -1 });
Тем не менее, возникают проблемы, если вы пытаетесь отсортировать в порядке убывания страны, но в порядке возрастания города:
db.user.find().sort({ country: -1, city: 1 });
Наш индекс не может быть использован, поэтому вы должны либо запретить неиндексированные вторичные критерии сортировки, либо создать другой подходящий индекс:
db.user.createIndex({ country: -1, city: 1 });
Опять же, это также может быть использовано для запросов, которые изменили порядок:
db.user.find().sort({ country: 1, city: -1 });
5. Создайте два или более объектов соединения
При создании приложения вы можете повысить эффективность с помощью одного постоянного объекта подключения к базе данных, который повторно используется для всех запросов и обновлений.
MongoDB запускает все команды в том порядке, в котором получает их от каждого клиентского соединения. В то время как ваше приложение может выполнять асинхронные вызовы в базу данных, каждая команда синхронно ставится в очередь и должна быть выполнена до того, как будет обработана следующая. Если у вас сложный запрос, выполнение которого занимает десять секунд, никто другой не сможет одновременно взаимодействовать с вашим приложением по одному и тому же соединению.
Производительность может быть улучшена путем определения более одного объекта подключения к базе данных. Например:
- один для обработки большинства быстрых запросов
- один для обработки медленных вставок и обновлений документов
- один для обработки сложных отчетов.
Каждый объект рассматривается как отдельный клиент базы данных и не будет задерживать обработку других. Приложение должно оставаться отзывчивым.
6. Установите максимальное время выполнения
Команды MongoDB выполняются столько, сколько им нужно. Медленно выполняющийся запрос может задержать других, и ваше веб-приложение может в конечном итоге истечь. Это может привести к различным странным проблемам нестабильности в программах Node.js, которые с радостью продолжают ожидать асинхронного обратного вызова.
Вы можете указать ограничение по времени в миллисекундах с помощью maxTimeMS () : например, разрешить 100 миллисекундам (одну десятую секунды) запрашивать документы в коллекции user
city
db.user.find({ city: /^A.+/i }).maxTimeMS(100);
Вы должны установить разумное значение maxTimeMS
К сожалению, MongoDB не позволяет вам определять глобальное значение тайм-аута, и оно должно быть установлено для отдельных запросов (хотя некоторые библиотеки могут применять значение по умолчанию автоматически).
7. Перестройте свои индексы
Если вы удовлетворены тем, что ваша структура эффективна, но запросы все еще работают медленно, попробуйте перестроить индексы для каждой коллекции. Например, перестройте индексы коллекции user
mongo :
db.user.reIndex();
Если ничего не помогает, вы можете рассмотреть восстановление базы данных, чтобы найти и исправить любые проблемы. Это следует считать последним средством, когда все другие варианты были исчерпаны. Я бы порекомендовал полное резервное копирование, используя mongodump или другой подходящий метод, прежде чем продолжить .
Пусть все ваши запросы MongoDB остаются быстрыми и эффективными! Пожалуйста, дайте мне знать, если у вас есть дополнительные советы по производительности.