Средний

Проблема N+1 и батчинг

Урок 3 из 4 в курсе Производительность баз данных

Содержание курса (3/4)

Проблема N+1 и батчинг

N+1 — классическая ловушка ORM: один запрос превращается в сотни лишних.

Почему это важно: N+1 — самая частая причина медленных endpoint'ов в backend-приложениях.

Главная идея

Получив N родительских записей, код в цикле делает по запросу за каждым ребёнком — итого N+1 запрос. Решение — eager loading или батчинг.

Как это выглядит на практике

  1. GET /users возвращает 50 пользователей.
  2. Для каждого ORM лениво подгружает профиль → 50 запросов SELECT.
  3. Запрос на endpoint занимает 1.5 секунды вместо 30 мс.
  4. Используем includes / preload / JOIN — остаётся 2 запроса.

Что происходит под капотом

  • Lazy loading — связи подгружаются по обращению, отсюда и N+1.
  • Eager loading — связи подгружаются сразу одним или двумя запросами.
  • DataLoader / batch loaders — асинхронный батчинг запросов в одном тике.
  • JOIN не всегда быстрее: на больших связях лучше отдельный preload.

Типичные ошибки и заблуждения

  • Ошибка: 'у нас же ORM, он сам разберётся'. Не разберётся — N+1 не заметить без логов.
  • Ошибка: всегда делать JOIN. На многих связях он порождает огромный декартов результат.
  • Ошибка: includes решает всё. Иногда нужны explicit batch loaders.
  • Ошибка: один лишний запрос = ничего страшного. Под нагрузкой это сотни запросов в секунду.

Ключевые выводы

  • N+1 — это не баг ORM, это его удобство.
  • Включи логирование SQL и смотри глазами.
  • Eager loading — стандартный приём.
  • Батчинг запросов помогает в GraphQL и асинхронном коде.

Термины урока

ORM — Object-Relational Mapping, отображение объектов в таблицы.
Eager loading — заранее загружать связанные данные.
Lazy loading — загружать данные по обращению.
DataLoader — паттерн батчинга и кэширования запросов в рамках одного запроса пользователя.

Связь с работой backend-разработчика

Любой backend-разработчик обязан замечать N+1 на ревью — это самый дешёвый способ поднять производительность.

Мини-разбор реальной ситуации

Мобильное приложение жаловалось на медленную ленту. В логах нашли 450 SQL-запросов на один endpoint. Один includes — и осталось 3 запроса, время ответа упало с 2.1 с до 80 мс.

Что запомнить

  • N+1 = самый частый враг.
  • Логируй SQL в dev-окружении.
  • Eager loading — твой друг.

Итог

Победа над N+1 — это первый шаг к быстрому backend.

Комментарии к уроку

Войдите, чтобы оставить комментарий.

Пока нет комментариев — будьте первым.