JavaRush /Курси /Модуль 4: FastAPI /Оптимізація запитів у гібридних архітектурах

Оптимізація запитів у гібридних архітектурах

Модуль 4: FastAPI
Рівень 10 , Лекція 8
Відкрита

Уявіть, що ви будуєте крутий інтернет-магазин. У вас є SQL-база для керування замовленнями й складом, а NoSQL-база відповідає за рекомендації товарів. Все працює, як годинник, поки на ваш сайт не хлине натовп покупців у Чорну п'ятницю. Дані починають оброблюватися повільно, користувачі зляться, а керівництво панікує. Ось тоді ви й розумієте: Оптимізація запитів — це не просто фан-сервіс для програмістів.

Оптимізація запитів дозволяє:

  • Зменшити навантаження на сервери.
  • Зробити застосунок швидшим (користувачі люблять швидкість!).
  • Заощадити гроші на інфраструктурі.

У гібридних архітектурах, де ми взаємодіємо одразу з різними типами баз даних (SQL і NoSQL), задачі ускладнюються, але розібратися в усьому цілком реально.


Основи оптимізації запитів

  1. Індекси: твої найкращі друзі.
    Індекси — це як швидкісна траса для запитів. Якщо правильно їх налаштувати, сервер зможе знаходити потрібні дані набагато швидше.

    Приклад індексу в 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), але не перестарайтеся: занадто багато індексів збільшує час запису даних.

  2. Розділяй і володарюй: горизонтальне та вертикальне партиціювання.
    Партиціювання даних — це розбиття великих таблиць (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))
            

Найкращі практики роботи з запитами в гібридній архітектурі

  1. Розділіть відповідальність. Нехай SQL обробляє транзакційні запити, а NoSQL займається аналітикою або зберіганням великих обсягів даних.
  2. Використовуйте кешування на всіх рівнях: запитів, результатів або навіть на рівні застосунків.
  3. Регулярно профілюйте ваші запити, щоб знаходити вузькі місця. Для PostgreSQL використовуйте EXPLAIN ANALYZE. Для MongoDB — профайлер запитів db.system.profile.
  4. Перевіряйте індекси: вони гнучкі, але вимагають ресурсів при записі. Включайте тільки ті, які справді потрібні.

Тепер ви готові впоратися з повільними запитами в будь-якій гібридній системі. Пам'ятайте: оптимізація — це не фокус, який ви робите раз і назавжди. Це процес, який варто повторювати в міру росту вашого проєкту.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ