Ответ вслух Middle Eloquent, N+1 и миграции спрашивают часто обновлено

N+1 в Laravel: поиск и лечение

Вопрос

Как вы находите и исправляете N+1 в Laravel-приложении?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

N+1 появляется, когда загружаем список моделей и в цикле или в шаблоне обращаемся к связи каждой. Признак: десятки одинаковых запросов, отличающихся только id, и время ответа растёт с длиной списка.

Что должно прозвучать в ответе

  • Признак: число запросов растёт с размером списка

  • Инструменты: Debugbar, Telescope, логирование запросов, preventLazyLoading

  • with и вложенные связи, load для дозагрузки

  • withCount, withSum, withExists вместо подсчёта в цикле

  • Выбор нужных колонок с ключами для сопоставления

  • API Resources и whenLoaded, чтобы не грузить связи неявно

Что могут спросить следом

  • Что будет, если в with указать связь, которая не нужна?
  • Как обнаружить N+1 в очередях и консольных командах?

Разбор

N+1 появляется, когда загружаем список моделей и в цикле или в шаблоне обращаемся к связи каждой. Признак: десятки одинаковых запросов, отличающихся только id, и время ответа растёт с длиной списка.

В разработке вижу это в Laravel Debugbar или Telescope, а надёжнее всего включаю Model::preventLazyLoading() для непродакшен-окружений: ленивая загрузка начинает бросать исключение, и тесты сразу падают.

Лечу жадной загрузкой: with(['author', 'comments.user']), а для уже полученной коллекции load. Если нужна только цифра, беру withCount или withSum, тогда связь вообще не грузится. Если ограничиваю колонки, не забываю ключи для сопоставления. В API Resources использую whenLoaded, чтобы ресурс не провоцировал ленивую загрузку. В консольных командах и заданиях то же самое, плюс обработка порциями через chunkById.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.