Тюнінг MongoDB: WiredTiger, індекси, запити
Уявіть: сервер з 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 → range
db.orders.createIndex({ user_id: 1, status: 1, created_at: -1 })
Повільні агрегації з $lookup
Найчастіша помилка — $match після $lookup. Оптимальний порядок:
db.orders.aggregate([
{ $match: { status: "completed", created_at: { $gte: ISODate(new Date().getFullYear() + '-01-01') } } },
{ $lookup: { from: "users", localField: "user_id", foreignField: "_id", as: "user",
pipeline: [{ $match: { country: "UA" } }, { $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-3 рази після оптимізації. Наприклад, один клієнт зекономив 450 000 грн за перший рік. Наша команда гарантує, що такий підхід до індексації скорочує час відповіді вдвічі порівняно з типовою конфігурацією. Середня економія клієнтів за перший рік — 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 — отримайте консультацію інженера та план оптимізації.







