MongoDB — відмінна гнучко масштабована база даних, але ніхто не застрахований від того, що колись твій сервер не почне просити передишки. Особливо, якщо ти забув подбати про те, щоб запити до бази були оптимізовані. Сьогодні розберемо, як правильно проєктувати схеми даних, створювати індекси, користуватися інструментами діагностики й аналізувати вузькі місця. Усе — щоб твоя база даних літала, а не жалілася.
Денормалізація даних
На відміну від реляційних баз даних, у MongoDB денормалізація часто є звичною практикою. Це означає, що дані іноді дублюються для пришвидшення читання.
Приклад:
Замість того, щоб тягнути звʼязки в стилі SQL через JOIN, ти можеш зберігати вкладені структури прямо в документі.
# Замість такого підходу (у стилі SQL)
{
"_id": 1,
"user_id": 42,
"order_details": [101, 102, 103]
}
# Можна зберігати так (денормалізований варіант):
{
"_id": 1,
"user_id": 42,
"orders": [
{"id": 101, "product": "Book", "price": 15.0},
{"id": 102, "product": "Laptop", "price": 899.99},
{"id": 103, "product": "Pen", "price": 1.5}
]
}
Перевага другого підходу очевидна: зайві операції для обʼєднання даних не потрібні, і читання відбувається швидше за рахунок локальності інформації.
Використання документів розумного розміру
MongoDB дозволяє зберігати документи розміром до 16 МБ, але це не означає, що треба юзати всі 16 МБ для одного документа. Великі документи можуть значно сповільнити читання й оновлення даних.
Рекомендується:
- Розбивати дані на логічні блоки;
- Використовувати посилання (
_idповʼязаного документа) у випадку, якщо дані занадто обʼємні.
Індексація: основа оптимізації запитів
Індекси — це структура даних, яка дозволяє MongoDB швидше виконувати запити. Без індексу MongoDB сканує всю колекцію (collection scan), що може бути катастрофічно довго.
Приклад створення індексу:
# Створення індексу на поле "username"
await collection.create_index("username")
Тепер будь-які запити, у яких використовується фільтр username, відбуватимуться швидше.
MongoDB підтримує кілька видів індексів:
- Одиночний індекс (Single Field):
- Індекс на одне поле.
- Використовується для фільтрації по одному критерію.
await collection.create_index("email") - Складений індекс (Compound Index):
- Індекс відразу на кілька полів.
- Користь для складних запитів.
await collection.create_index([("first_name", 1), ("last_name", 1)]) - Текстовий індекс (Text Index):
- Використовується для повнотекстового пошуку.
await collection.create_index([("description", "text")]) - Індекс геопросторових даних (Geospatial Index):
- Для запитів з географічними координатами.
await collection.create_index([("location", "2dsphere")])
Коли індекси шкодять?
Індекси не безкоштовні: вони збільшують використання памʼяті і уповільнюють операції вставки/оновлення. Рекомендується створювати індекси тільки для тих полів, які часто використовуються в фільтрах або сортуванні.
Інструменти моніторингу та діагностики
Кожного разу, коли пишеш запит, важливо розуміти, скільки часу він займає і як MongoDB його виконує. Для цього є метод explain().
Приклад використання:
result = await collection.find({"username": "johndoe"}).explain()
print(result)
У методу explain() є три режими:
- queryPlanner: показує, як MongoDB планує запит.
- executionStats: дає статистику виконання.
- allPlansExecution: показує всі плани виконання запиту.
Якщо запити йдуть повільно, перевір:
- Чи використовуються індекси?
- Чи є індекси на правильних полях?
- Чи немає надто великих документів?
Масштабування MongoDB: реплікація та шардінг
Реплікація використовується для забезпечення доступності даних. MongoDB підтримує реплікацію через Replica Set. Це система, де одна база даних є основною (primary), а інші — репліками (secondary).
Переваги реплікації:
- Доступність даних при відмові primary.
- Підвищення продуктивності читання за рахунок розподілу операцій між secondary репліками.
Приклад налаштування реплікації можна знайти в офіційній документації MongoDB.
Для горизонтального масштабування MongoDB підтримує шардінг. Це метод, при якому дані розподіляються по різних серверах (шардах).
Коли використовувати шардінг?
- Коли колекції містять мільйони або навіть мільярди документів.
- Коли запити до бази починають викликати блокування.
Налаштування шардінгу вимагає вибору правильного ключа шардінгу (shard key). Цей вибір надзвичайно важливий, бо впливає на балансування даних між серверами.
Найкращі практики оптимізації запитів
- Використовуй фільтри по індексу:
- Переконайся, що запит фільтрує дані по полях, які мають індекс.
- Уникай
collection scan:- Запити, що не використовують індекси, сканують всю колекцію і дуже повільні.
- Обмежуй кількість повернених полів:
- Не запитуй більше даних, ніж потрібно.
- Використовуй проекцію:
# Повернути тільки поле "username" result = await collection.find({}, {"username": 1}) - Аналізуй часто використовувані запити:
- Застосовуй
explain()для всіх найважливіших запитів.
- Застосовуй
- Використовуй агрегації:
- Для складних операцій використовуй
aggregate. Це потужний інструмент, який дозволяє MongoDB виконувати операції прямо на сервері.
pipeline = [ {"$match": {"status": "active"}}, {"$group": {"_id": "$category", "total": {"$sum": 1}}} ] result = await collection.aggregate(pipeline).to_list(length=100) - Для складних операцій використовуй
MongoDB, як і будь-який інструмент, вимагає вмілого поводження. Використовуй індекси, перевіряй продуктивність запитів і не бійся експериментувати з масштабуванням. Наступного разу, коли почуєш, що "запити гальмують", ти будеш знати, що робити.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ