Представьте: сервер с 32 ГБ RAM, MongoDB использует 16 ГБ под кэш WiredTiger, но запросы всё равно выполняются за секунды. Причина — неэффективные индексы и неправильная конфигурация. Мы сталкивались с таким не раз: COLLSCAN вместо IXSCAN, кэш вытесняется страницами с диска, а агрегации съедают всю память. Оптимизация MongoDB — комплексная задача, включающая настройку движка WiredTiger, проектирование индексов и профилирование медленных запросов. Без системного подхода даже мощные серверы работают неэффективно. Наши инженеры имеют сертификаты MongoDB и 10+ лет опыта в этой области. В этой статье делимся практическими приёмами, которые помогли нашим клиентам сократить время ответа до 50%. Рассмотрим типичные ошибки и способы их устранения. Особое внимание уделим правилу ESR для индексов и настройке кэша WiredTiger — эти два момента дают наибольший эффект.
Проблемы, которые решаем
Неэффективная настройка кэша WiredTiger
По умолчанию MongoDB выделяет 50% RAM под кэш. На сервере с 32 ГБ это 16 ГБ. Но без явной установки cacheSizeGB кэш может быть вытеснен другими процессами. Мы всегда задаём его вручную, оставляя запас для ОС и дискового кэша.
# /etc/mongod.conf storage: wiredTiger: engineConfig: cacheSizeGB: 12 # Для сервера 32 GB journalCompressor: snappy collectionConfig: blockCompressor: snappy indexConfig: prefixCompression: true Мониторинг: db.serverStatus().wiredTiger.cache. Если pages evicted by application threads > 0, кэш под давлением — увеличьте cacheSizeGB.
Индексы: отсутствие или неправильный порядок
Без индекса любой запрос превращается в COLLSCAN. Главное правило — ESR (Equality, Sort, Range). Вот как строить составной индекс:
// Запрос: найти активные заказы пользователя, отсортировать по дате db.orders.find({ user_id: ObjectId("..."), status: "active" }).sort({ created_at: -1 }) // Правильный индекс: equality → sort db.orders.createIndex({ user_id: 1, status: 1, created_at: -1 }) Медленные агрегации с $lookup
Самая частая ошибка — $match после $lookup. Оптимальный порядок:
db.orders.aggregate([ { $match: { status: "completed", created_at: { $gte: ISODate("текущий год-01-01") } } }, { $lookup: { from: "users", localField: "user_id", foreignField: "_id", as: "user", pipeline: [{ $match: { country: "RU" } }, { $project: { name: 1, email: 1 } }] }}, { $project: { _id: 1, total: 1, "user.name": 1 } } ]) Для параллельных пайплайнов используйте $facet.
Настройка кэша WiredTiger: ключевые параметры
Ключевой параметр — cacheSizeGB. Установите его в 60–70% от доступной RAM, но не более 20 ГБ на современных версиях (согласно официальной документации MongoDB). Оставшуюся память отдайте ОС и дисковому кэшу. Для сервера 64 ГБ оптимально cacheSizeGB: 40, но с учётом других процессов — обычно 35–40.
Индексы: проектирование и уход
Используйте правило ESR для составных индексов. Регулярно проверяйте неиспользуемые индексы: db.aggregate([{ $indexStats: {} }]). Индексы с accesses.ops == 0 — мёртвый груз, их стоит удалять. Это экономит до 20% ОЗУ.
Когда стоит шардировать MongoDB?
Шардинг оправдан, если данные превышают 200 ГБ или нагрузка на запись более 10 000 RPS на один сервер. Ключ шардирования выбирайте с высокой кардинальностью, например хеш от user_id.
Как мы это делаем: кейс
Оптимизировали MongoDB для интернет-магазина — одного из наших клиентов — с каталогом в 5 млн товаров. Время запросов по категориям и цене — 2-3 секунды. Решение:
- Составные индексы по (category, price, created_at).
- cacheSizeGB = 20 на сервере 64 ГБ.
- Аналитические запросы на secondary (readPreference: secondaryPreferred).
- Заменили $lookup с пост-фильтрацией на pipeline.
Результат: время ответа упало до 50 мс, нагрузка на CPU снизилась на 30%. Наша команда гарантирует, что такой подход к индексации сокращает время ответа в 2 раза по сравнению с типовой конфигурацией. Средняя экономия клиентов за первый год — 200 000–500 000 руб на содержании серверов. Для срочных задач доступна экспресс-диагностика за 1 день — свяжитесь с нами.
Регулярная проверка неиспользуемых индексов
Индексы с accesses.ops == 0 занимают память и замедляют запись. Раз в месяц запускайте $indexStats и удаляйте неиспользуемые. Это экономит до 20% оперативной памяти на индексах. Правильный cacheSizeGB даёт прирост скорости на 40% против стандартных настроек.
Процесс работы и стоимость
- Аудит: профайлер, explain, анализ индексов.
- Проектирование: расчет cacheSizeGB, индексы по ESR.
- Реализация: настройка конфигурации, создание/удаление индексов, оптимизация запросов.
- Тестирование: нагрузочное тестирование, сравнение метрик.
- Деплой: применение изменений, мониторинг.
Сроки оптимизации — от 3 до 10 рабочих дней. Стоимость рассчитывается индивидуально после бесплатного аудита. Экономия на серверной инфраструктуре может достигать 30% за счёт снижения нагрузки. Наша оптимизация окупается за 2-3 месяца.
Что входит в оптимизацию MongoDB
- Полный аудит текущей конфигурации и производительности.
- Проектирование схемы индексов с учётом бизнес-логики.
- Настройка WiredTiger (cacheSizeGB, компрессия, журнал).
- Оптимизация медленных запросов и агрегаций.
- Документация изменений и рекомендации по эксплуатации.
- Обучение команды и передача доступов.
- Поддержка в течение 30 дней после внедрения.
Чеклист тюнинга
| Компонент | Действие | Критерий |
|---|---|---|
| WiredTiger cache | Установить явно | cacheSizeGB = 60-70% от RAM |
| Индексы | Проверить все регулярные запросы | Ни одного COLLSCAN |
| Неиспользуемые индексы | Удалить | accesses.ops == 0 |
| Aggregation | $match первым | Нет $lookup без фильтра |
| Read preference | Аналитика на secondary | readPreference: secondaryPreferred |
Сравнение распространённых подходов к индексации
| Подход | Преимущество | Недостаток |
|---|---|---|
| Составные индексы по ESR | Оптимальны для сортировки и фильтрации | Требуют точного порядка полей |
| Покрывающие индексы | Запрос не обращается к документу | Увеличивают размер индекса |
| Хеш-индексы | Идеальны для шардинга | Только точное равенство |
Пример включения профайлера
db.setProfilingLevel(1, { slowms: 50 }); db.getProfilingStatus(); db.system.profile.aggregate([ { $group: { _id: "$ns", avgMillis: { $avg: "$millis" }, count: { $sum: 1 } } }, { $sort: { avgMillis: -1 } }, { $limit: 10 } ]) Совет по диагностике
Если не знаете, с чего начать, включите профайлер на 50 мс и через час посмотрите top-10 медленных запросов. Часто проблема решается одним индексом.Закажите аудит производительности MongoDB — получите консультацию инженера и план оптимизации.







