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"Process record with 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"Processing record 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 для хранения часто запрашиваемых данных.
  • Проектируйте модели для масштабируемости: партиционируйте данные или используйте шардирование, если это необходимо.
  • Тестируйте на реальных данных: убедитесь, что ваши запросы работают быстро и эффективно даже с большими объёмами данных.
1
Задача
Модуль 4: FastAPI, 11 уровень, 8 лекция
Недоступна
Реализация батчевых операций
Реализация батчевых операций
1
Задача
Модуль 4: FastAPI, 11 уровень, 8 лекция
Недоступна
Параллельная обработка данных
Параллельная обработка данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ