Ответ вслух Middle Django ORM и N+1 спрашивают часто обновлено

Как найти и исправить N+1 в Django

Вопрос

Страница списка заказов тормозит. Как вы найдёте и исправите проблему N+1?

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

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

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

Коротко

Образцовый ответ: «Признак N+1: число SQL-запросов на странице растёт вместе с числом объектов. Локально смотрю django-debug-toolbar, в тестах assertNumQueries, в проде трейсы APM, где видны сотни одинаковых запросов.

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

  • Симптом: число запросов растёт с числом объектов

  • Поиск: django-debug-toolbar, логи SQL, `assertNumQueries` в тестах, APM

  • `select_related` для FK и OneToOne (JOIN)

  • `prefetch_related` и `Prefetch` для ко многим

  • Аннотации вместо подсчётов в цикле, `only` и `values` для лишних полей

  • Закрепить тестом на число запросов

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

  • Когда JOIN хуже, чем prefetch?
  • Как N+1 проявляется в сериализаторах DRF?

Разбор

Образцовый ответ: «Признак N+1: число SQL-запросов на странице растёт вместе с числом объектов. Локально смотрю django-debug-toolbar, в тестах assertNumQueries, в проде трейсы APM, где видны сотни одинаковых запросов. Дальше смотрю, какие связи трогаются в цикле, шаблоне или сериализаторе. Для ForeignKey и OneToOne добавляю select_related: связанные объекты придут одним JOIN. Для «ко многим», то есть позиций заказа или тегов, prefetch_related: второй запрос с IN по всем id. Если для вложенных объектов тоже нужны связи или фильтр, использую Prefetch со своим QuerySet. Если в цикле считается количество или сумма, заменяю на annotate(Count(...)). Лишние поля отсекаю only или values. В конце закрепляю результат тестом с assertNumQueries, чтобы регрессия не вернулась. JOIN хуже prefetch, когда связь «ко многим» размножает строки основного запроса».

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

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