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

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

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

Ви колись намагалися завантажити таблицю з мільйонами записів через .filter() і отримали HTTP 500? Якщо таке траплялося, то ви вже зіткнулися зі світом великих даних. На цьому рівні розробка переходить від «ну, працює» до «а скільки це займає часу?». Проблеми тут бувають такі:

  • Повільне виконання запитів: при збільшенні кількості записів у таблиці навіть базові запити можуть стати повільними.
  • Проблеми з пам'яттю: одночасно витягнути 10 мільйонів записів? Ваш сервер може сказати «пока».
  • Проблеми з транзакціями: внесення змін у великих обсягах даних може блокувати базу даних.

Але не партеся — з правильними інструментами і підходами можна змусити навіть найбільші таблиці працювати швидко та ефективно.


Масштабованість і продуктивність

Масштабованість — це здатність застосунку обробляти зростаючі обсяги даних або збільшену кількість запитів. В контексті баз даних це включає:

  1. Вертикальна масштабованість: збільшуємо потужність однієї бази даних (додаємо CPU, RAM тощо).
  2. Горизонтальна масштабованість: розподіляємо дані між кількома машинами (шардинг, реплікація).

Продуктивність — це час відгуку вашої бази даних. Чим менша затримка виконання запиту — тим краще. Продуктивність залежить від організації даних, індексів, ключів і підходів до виконання запитів.


Інструменти для оптимізації

SQLAlchemy на цьому етапі стає вашим вірним другом, але тільки якщо ви знаєте, як змусити його працювати на вас. Давайте розглянемо основні підходи:

batch-операції

Подумайте: чи завжди потрібно обробляти 10 мільйонів записів за один запит? Зазвичай — ні. Тут на допомогу приходять batch-операції.

Приклад:


from sqlalchemy.orm import Session
from my_project.models import BigTable

def process_in_batches(session: Session, batch_size: int = 1000):
    # Витягування даних батчами
    offset = 0
    while True:
        # Витягуємо дані порціями
        batch = session.query(BigTable).offset(offset).limit(batch_size).all()
        if not batch:
            break
        # Обробляємо кожний запис
        for record in batch:
            print(f"Обробляю запис з ID: {record.id}")
        
        offset += batch_size

Зверніть увагу: батчі допомагають економити пам'ять, бо ви працюєте лише з частиною даних у кожен момент часу.

Паралельна обробка

Якщо ваш сервер має кілька ядер, то чому б не використати їх всі? Тут можуть бути корисні такі інструменти, як concurrent.futures або сторонні бібліотеки, наприклад, celery.

Приклад:


from concurrent.futures import ThreadPoolExecutor
from sqlalchemy.orm import Session
from my_project.models import BigTable

def process_record(record):
    print(f"Обробляю запис з ID: {record.id}")

def parallel_processing(session: Session, batch_size: int = 1000):
    with ThreadPoolExecutor() as executor:
        offset = 0
        while True:
            batch = session.query(BigTable).offset(offset).limit(batch_size).all()
            if not batch:
                break
            # Надсилаємо батч на обробку
            executor.map(process_record, batch)
            offset += batch_size

Такий підхід суттєво підвищує швидкість обробки даних.

Індексація даних

Приклад створення індексу в SQLAlchemy:


from sqlalchemy import Index
from my_project.models import BigTable

# Створюємо індекс
Index('idx_column_name', BigTable.column_name)

Для часто використовуваних фільтрів створюйте індекси, щоб зменшити час виконання запитів.


Приклади оптимізації роботи з великими обсягами даних

  1. Асинхронне завантаження даних (FastAPI)

    Коли працюєш з великими обсягами даних і FastAPI, використання асинхронного доступу до бази може кардинально покращити продуктивність.

    
    from sqlalchemy.ext.asyncio import AsyncSession
    from sqlalchemy.future import select
    from my_project.models import BigTable
    
    async def fetch_large_dataset(session: AsyncSession):
        result = await session.execute(select(BigTable).limit(1000))
        records = result.scalars().all()
        return records
    

    Асинхронність дозволяє системі продовжити виконання інших задач, поки дані витягуються.

  2. Використання агрегатів і групувань

    Іноді замість витягування всіх даних вигідніше виконати агрегатні запити прямо в базі даних.

    
    from sqlalchemy import func
    from sqlalchemy.orm import Session
    from my_project.models import BigTable
    
    def get_aggregated_data(session: Session):
        result = session.query(
            BigTable.category,
            func.count(BigTable.id),
            func.avg(BigTable.value)
        ).group_by(BigTable.category).all()
        return result
    

    Це зменшить навантаження на застосунок і дозволить базі даних зробити всю важку роботу.

  3. Шардинг даних

    Шардинг (sharding) — це розподіл таблиці на кілька баз даних. Наприклад, у вас є таблиця з даними за кілька років. Ви можете розбити дані по роках так, щоб кожна база обслуговувала свій "кусок".

    
    # Псевдокод для роботи з різними базами даних
    db_map = {
        '2023': create_engine('postgresql://user:password@host/database_2023'),
        '2024': create_engine('postgresql://user:password@host/database_2024')
    }
    
    def get_engine_by_year(year):
        return db_map[str(year)]
    

Приклади з реальних проєктів

  1. E-commerce застосунки: сортування мільйонів товарів, обчислення рейтингів і відображення фільтрів — все це потребує оптимізації запитів.
  2. Аналіз даних: застосунки, що працюють з великими масивами даних, використовують агрегації і партиціонування для швидкості.
  3. Соціальні мережі: стрічки новин часто будуються через батчі і кешування, щоб обробляти мільйони записів.

Практичні поради

  • Завжди профілюйте запити: використовуйте інструменти, такі як SQLAlchemy Profiling або EXPLAIN в PostgreSQL.
  • Уникайте SELECT *: вибирайте лише ті поля, які вам реально потрібні.
  • Використовуйте кешування: наприклад, Redis для зберігання часто запитуваних даних.
  • Проєктуйте моделі для масштабованості: партиціюйте дані або використовуйте шардинг, якщо це потрібно.
  • Тестуйте на реальних даних: переконайтеся, що ваші запити працюють швидко й ефективно навіть з великими обсягами даних.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ