Уявіть, що ви будуєте крутий інтернет-магазин. У вас є SQL-база для керування замовленнями й складом, а NoSQL-база відповідає за рекомендації товарів. Все працює, як годинник, поки на ваш сайт не хлине натовп покупців у Чорну п'ятницю. Дані починають оброблюватися повільно, користувачі зляться, а керівництво панікує. Ось тоді ви й розумієте: Оптимізація запитів — це не просто фан-сервіс для програмістів.
Оптимізація запитів дозволяє:
- Зменшити навантаження на сервери.
- Зробити застосунок швидшим (користувачі люблять швидкість!).
- Заощадити гроші на інфраструктурі.
У гібридних архітектурах, де ми взаємодіємо одразу з різними типами баз даних (SQL і NoSQL), задачі ускладнюються, але розібратися в усьому цілком реально.
Основи оптимізації запитів
- Індекси: твої найкращі друзі.
Індекси — це як швидкісна траса для запитів. Якщо правильно їх налаштувати, сервер зможе знаходити потрібні дані набагато швидше.
Приклад індексу в PostgreSQL:
Тепер запит типу-- Створюємо індекс на колонці "email" таблиці "users" CREATE INDEX idx_users_email ON users(email);SELECT * FROM users WHERE email = 'example@example.com';виконуватиметься значно швидше.
Приклад індексу в MongoDB:# Створюємо індекс на полі "user_id" у колекції "orders" db.orders.create_index("user_id")Індекси можна й потрібно використовувати в обох базах (SQL і NoSQL), але не перестарайтеся: занадто багато індексів збільшує час запису даних.
- Розділяй і володарюй: горизонтальне та вертикальне партиціювання.
Партиціювання даних — це розбиття великих таблиць (SQL) або колекцій (NoSQL) на частини.
Горизонтальне партиціювання:
ви розділяєте дані по рядках. Наприклад, якщо у вас є таблиця користувачів з мільйонами записів, можна розбити їх на основі ID:- Від 1 до 500 000 в одній таблиці.
- Від 500 001 до 1 000 000 — в іншій.
Вертикальне партиціювання:CREATE TABLE users_1_to_500k PARTITION OF users FOR VALUES FROM (1) TO (500000);
тут ви ділите дані за колонками. Наприклад, у таблиці замовлень можна винести часто використовувані поля (order_id,status) в одну таблицю, а рідко використовувані (delivery_notes,archive) — в іншу.
У MongoDB:
у NoSQL підхід трохи відрізняється: партиції створюються на рівні шардингу. Для цього ви вказуєте ключ шардування, наприклад:
Тепер замовлення автоматично розподіляються по регіонах.sh.enableSharding("myDatabase") sh.shardCollection("myDatabase.orders", { "region": 1 })
Кешування: чому б не запам'ятати результати?
Кешування дозволяє уникнути частих звернень до бази даних. Наприклад, дані, які рідко змінюються (каталог товарів або статистика), можна кешувати.
Redis — відмінний інструмент для проміжного зберігання даних. Ми можемо кешувати результати з PostgreSQL і MongoDB.
Приклад у FastAPI:
import redis
import json
from fastapi import FastAPI
app = FastAPI()
redis_client = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)
@app.get("/product/{product_id}")
async def get_product(product_id: int):
cached_product = redis_client.get(f"product:{product_id}")
if cached_product:
return json.loads(cached_product)
# Якщо в кеші немає даних, йдемо в базу даних
product = get_product_from_postgresql(product_id)
redis_client.set(f"product:{product_id}", json.dumps(product), ex=3600) # Кешуємо на годину
return product
Тепер ми суттєво зменшуємо навантаження на PostgreSQL!
Оптимізація запитів у гібридних системах: основи
- Використовуйте тільки потрібні дані.
Не варто запитувати більше даних, ніж потрібно. Наприклад, замість того щоб робити:
Краще явно вказувати потрібні колонки:SELECT * FROM orders;SELECT order_id, status FROM orders; - Асинхронні запити для роботи з MongoDB.
Коли ви взаємодієте з MongoDB через FastAPI, важливо використовувати асинхронні запити для підвищення продуктивності.
Приклад:from motor.motor_asyncio import AsyncIOMotorClient client = AsyncIOMotorClient('mongodb://localhost:27017') db = client.mydatabase async def get_orders_by_user(user_id): return await db.orders.find({"user_id": user_id}).to_list(length=100)
Агрегації та аналітика в гібридних системах
Якщо ваші дані знаходяться в різних базах, вам, скоріше за все, знадобиться їх агрегувати для об'єднаного аналізу. Як це виглядає?
У PostgreSQL використовуйте складні запити з групуванням:
SELECT user_id, COUNT(order_id) AS num_orders
FROM orders
GROUP BY user_id
HAVING COUNT(order_id) > 5;
У MongoDB використовуйте агрегації:
pipeline = [
{"$match": {"status": "completed"}},
{"$group": {"_id": "$user_id", "total": {"$sum": "$amount"}}}
]
results = db.orders.aggregate(pipeline)
Інколи вам потрібні дані з обох баз. Один із підходів — спочатку зібрати їх окремо, а потім об'єднати на рівні Python:
sql_results = get_data_from_postgresql()
nosql_results = get_data_from_mongodb()
combined_results = merge_results(sql_results, nosql_results)
Кешування складних запитів через ETL
Якщо у вас є важкі запити на рівні гібридної архітектури, можливо, варто використовувати ETL (Extract, Transform, Load) процеси. Наприклад, раз на день витягувати дані з SQL і NoSQL у проміжне сховище (той самий Redis).
Приклад:
- На рівні PostgreSQL збираємо щоденні замовлення:
SELECT user_id, SUM(amount) as total_amount FROM orders WHERE order_date = CURRENT_DATE GROUP BY user_id; - Результати записуємо в Redis для швидкого доступу:
redis_client.set("daily_orders", json.dumps(sql_results))
Найкращі практики роботи з запитами в гібридній архітектурі
- Розділіть відповідальність. Нехай SQL обробляє транзакційні запити, а NoSQL займається аналітикою або зберіганням великих обсягів даних.
- Використовуйте кешування на всіх рівнях: запитів, результатів або навіть на рівні застосунків.
- Регулярно профілюйте ваші запити, щоб знаходити вузькі місця. Для PostgreSQL використовуйте
EXPLAIN ANALYZE. Для MongoDB — профайлер запитівdb.system.profile. - Перевіряйте індекси: вони гнучкі, але вимагають ресурсів при записі. Включайте тільки ті, які справді потрібні.
Тепер ви готові впоратися з повільними запитами в будь-якій гібридній системі. Пам'ятайте: оптимізація — це не фокус, який ви робите раз і назавжди. Це процес, який варто повторювати в міру росту вашого проєкту.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ