В прошлом уроке мы создавали индексы, но поверили на слово, что они помогают. Сегодня проверим. У MongoDB можно спросить, как она выполнила запрос: перебирала ли всю коллекцию, пользовалась ли индексом, сколько документов прочитала ради ответа. Отвечает на это explain. Это последний урок кластера про агрегацию и индексы.
Навигатор и трек поездки
Навигатор перед поездкой показывает маршрут: «по проспекту, потом налево по мосту». А после поездки фитнес-браслет показывает трек: где ты ехал, сколько стоял в пробке, сколько километров намотал.
explain умеет и то, и другое:
explain()без аргумента показывает план: каким путём сервер собирается идти.explain('executionStats')выполняет запрос и показывает трек: сколько документов и ключей индекса пришлось прочитать.
В PostgreSQL это пара EXPLAIN и EXPLAIN ANALYZE, мы разбирали её в уроке про чтение планов.
Нужна большая коллекция
На девяти книгах любой запрос быстрый, и разница между планами не видна. Поэтому все примеры этого урока сняты с настоящего сервера на коллекции просмотров страниц: сто тысяч документов, пять тысяч разных пользователей, тридцать дней. Если у тебя есть свой сервер из урока про установку, создай такую же коллекцию:
use shopstats
const docs = []
for (let i = 0; i < 100000; i++) {
docs.push({ _id: i, book_id: (i % 9) + 1, user: 'u' + (i % 5000), day: i % 30 })
}
db.views.insertMany(docs)
Ответ insertMany получится длинным, сто тысяч номеров, его можно пролистать. У каждого пользователя ровно двадцать просмотров.
Запрос без индекса
Найдём просмотры одного пользователя и спросим, как сервер это сделал:
db.views.find({ user: 'u42' }).explain('executionStats')
Ответ большой, больше полусотни строк. Вот главные части, остальное заменено многоточием:
{
explainVersion: '1',
queryPlanner: {
namespace: 'shopstats.views',
parsedQuery: { user: { '$eq': 'u42' } },
...
winningPlan: {
isCached: false,
stage: 'COLLSCAN',
filter: { user: { '$eq': 'u42' } },
nss: 'shopstats.views',
direction: 'forward'
},
rejectedPlans: []
},
executionStats: {
executionSuccess: true,
nReturned: 20,
executionTimeMillis: 49,
totalKeysExamined: 0,
totalDocsExamined: 100000,
...
},
...
ok: 1
}
Читаем сверху вниз.
winningPlan это выбранный план. Его поле stage называет главный этап. COLLSCAN значит полный просмотр коллекции: сервер прочитал документы подряд и каждый проверил на user: 'u42'.
executionStats это трек поездки:
| Поле | Значение | Смысл |
|---|---|---|
nReturned | 20 | сколько документов вернул запрос |
totalKeysExamined | 0 | сколько записей индекса прочитано |
totalDocsExamined | 100000 | сколько документов прочитано |
executionTimeMillis | 49 | сколько миллисекунд шёл запрос |
Сто тысяч прочитанных документов ради двадцати нужных. Это главный сигнал: когда totalDocsExamined во много раз больше nReturned, запросу не хватает индекса. Время у тебя будет другим, оно зависит от машины. Счётчики документов и ключей от машины не зависят, поэтому смотреть в первую очередь стоит на них.
урок из курса
Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.