В этой главе мы коснёмся некоторых вопросов производительности, а также рассмотрим инструментарий, доступный разработчикам MongoDB. Мы не станем сильно погружаться в эти темы, но рассмотрим наиболее важные аспекты каждой.
Индексы
Индексы помогают фильтрации и сортировке, но занимают место и добавляют работу при записи. Список индексов коллекции показывает db.unicorns.getIndexes(), создание выполняется через createIndex:
db.unicorns.createIndex({name: 1});
И уничтожаются с помощью dropIndex:
db.unicorns.dropIndex({name: 1});
Уникальный индекс может быть создан, если во втором параметре установить unique в true:
db.unicorns.createIndex({name: 1}, {unique: true});
Можно создавать индексы над вложенными полями (опять же, используя точечную нотацию), либо над массивами. Также можно создавать составные индексы:
db.unicorns.createIndex({name: 1, vampires: -1});
Порядок вашего индекса (1 для восходящего и -1 для нисходящего) не играет роли в случае с простым индексом, однако он может быть существенен при сортировке или лимитировании с применением составных индексов.
На странице описания индексов можно найти дополнительную информацию.
Explain
Чтобы увидеть, используются ли индексы в ваших запросах, вызывайте у курсора метод explain:
db.unicorns.find().explain()
Для более подробного отчёта выполните db.unicorns.find().explain("executionStats"). Смотрите выбранные стадии плана, totalDocsExamined, totalKeysExamined и nReturned. Конкретная структура плана зависит от версии; названия старых BasicCursor и BtreeCursor больше не являются универсальными признаками.
Сравните план запроса с фильтром по индексированному полю и без него. Сам факт существования индекса не гарантирует его выбор планировщиком:
db.unicorns.find({name: 'Pilot'}).explain()
Запись без подтверждения
Подтверждение записи задаёт write concern. Нельзя считать, что все записи MongoDB по умолчанию не подтверждаются. При w: 0 приложение не получает обычное подтверждение применения записи; проверка ошибки после факта через старый getLastError не заменяет корректный API.
В примерах используйте подтверждённые операции и обрабатывайте исключения, включая конфликт уникального индекса. Требования к журналу и репликации задавайте исходя из необходимой устойчивости.
Шардинг
MongoDB поддерживает авто-шардинг. Шардинг — это подход к масштабируемости, когда отдельные части данных хранятся на разных серверах. Примитивный пример — хранить данные пользователей, чьё имя начинается на буквы A-M на одном сервере, а остальных — на другом. Возможности шардинга MongoDB значительно превосходят данный простой пример. Рассмотрение шардинга выходит за пределы данной книги, однако вы должны знать, что он существует, и вы должны воспользоваться им, когда ваши задачи выйдут за рамки одного сервера.
Репликация
Replica set содержит primary и secondary. Записи направляются в primary; чтение с secondary зависит от выбранного read preference и может вернуть отстающие данные. При отказе primary новый выбирается по правилам replica set, а не просто назначается произвольный второй сервер.
Главная задача репликации — доступность и отказоустойчивость. Она не заменяет резервные копии и не гарантирует ускорение любого чтения. Проектируйте число голосующих узлов и размещение по документации replica set; старое утверждение об одном общем арбитре для нескольких шардов удалено как некорректный эксплуатационный совет.
Статистика
Статистику базы данных можно получить с помощью вызова db.stats(). В основном информация касается размера вашей базы данных. Также можно получить статистику коллекции, например unicorns, с помощью вызова db.unicorns.stats(). Большая часть получаемой информации, опять же, касается размеров коллекции.
Веб-интерфейс
Устаревший встроенный HTTP-интерфейс и параметр rest=true не являются актуальным способом администрирования. Не открывайте порт 28017 и не включайте старые HTTP/REST-настройки ради этого примера. Для работы используйте mongosh, а для диагностики — штатные команды и инструменты своей версии.
Профайлер
Профилирование добавляет нагрузку и может записывать содержимое запросов. Сначала проверьте текущий уровень через db.getProfilingStatus(). Следующий эксперимент выполняйте только в учебной базе, затем верните прежние настройки. Не включайте уровень 2 на рабочей системе по инструкции из статьи.
Профайлер MongoDB можно включить с помощью следующего вызова:
db.setProfilingLevel(2);
Со включённым профайлером можно запустить команду:
db.unicorns.find({weight: {$gt: 600}});
И обратиться к профайлеру:
db.system.profile.find()
В результате мы увидим, что и когда запускалось, как много документов сканировалось, как много данных было возвращено.
Можно выключить профайлер, повторно вызвав setProfilingLevel, только передав 0 в качестве аргумента. Можно также передать 1 для профилирования запросов, выполняющихся дольше 100 миллисекунд. Также, можно вторым параметром передать время в миллисекундах:
//профилировать всё, что занимает более 1 секунды
db.setProfilingLevel(1, 1000);
Резервное копирование и восстановление
MongoDB Database Tools устанавливаются и версионируются отдельно от сервера. mongodump и mongorestore создают и восстанавливают BSON-дампы. Для рабочей базы отдельно проверяйте совместимость версий, права доступа и согласованность снимка при параллельных изменениях. Ниже показан учебный дамп одной базы:
Например, чтобы сделать резервную копию базы данных learn в папку backup, мы должны выполнить (разумеется не в консоли самой MongoDB, а просто в консоли операционной системы):
mongodump --uri="mongodb://127.0.0.1:27017" --db learn --out backup
Проверьте восстановление в отдельной базе learn_restore, чтобы не менять исходные учебные данные:
mongorestore --db learn_restore --collection unicorns backup/learn/unicorns.bson
Также, стоит упомянуть, что есть две утилиты mongoexport и mongoimport, предназначенные для экспорта и импорта данных в виде JSON и CSV. Например, можно получить результат в виде JSON следующим образом:
mongoexport --db learn --collection unicorns
И CSV:
mongoexport --db learn --collection unicorns --type=csv --fields=name,weight,vampires
mongoexport и mongoimport предназначены для обмена данными, а не полноценной резервной копии: вместе с документами важны типы, индексы и метаданные. Даже успешный mongodump ещё не доказывает пригодность копии — выполните тестовое восстановление и проверку содержимого. Метод резервирования рабочей системы выбирайте по топологии и требованиям к согласованности.
В этой главе
Перед оптимизацией измерьте запрос, проверьте план, размер коллекции и нагрузку. Индексирование, подтверждение записи, репликация, профилирование и резервирование решают разные задачи. Не меняйте настройки устойчивости ради неподтверждённого обещания скорости.