Получить доступ

Как найти и исправить проблему N+1 запросов в Django ORM?

MiddleВеб-фреймворкиDjango
Ещё спрашивают так:
  • Чем select_related отличается от prefetch_related?
Коротко

N+1 — один запрос за списком и по запросу на каждый элемент при обращении к связанному объекту в цикле. select_related — JOIN в одном запросе для ForeignKey и OneToOne. prefetch_related — отдельный запрос на связь и склейка в Python для ManyToMany и обратных связей.

  • Найти: django-debug-toolbar, логирование SQL, assertNumQueries в тестах
  • only/defer — не тянуть лишние колонки
  • Prefetch(queryset=...) — отфильтрованная предзагрузка

А ещё Агрегаты считать в базе: annotate(Count(...)), а не len() в цикле

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Откуда берётся. Ленивые связи ORM: order.customer загружается при первом обращении. В цикле по 100 заказам — 101 запрос. В шаблонах и сериализаторах DRF это особенно незаметно.

select_related строит SQL JOIN — подходит для связей «к одному». Для «ко многим» JOIN размножил бы строки, поэтому там prefetch_related: второй запрос WHERE order_id IN (...) и раскладка результатов по объектам в памяти.

Контроль в тестах:

def test_list(self):
    with self.assertNumQueries(2):
        self.client.get("/orders/")

Тот же класс проблем есть в любом ORM: в Hibernate — fetch join и EntityGraph, в SQLAlchemy — joinedload и selectinload.

Пример

# N+1: 1 запрос за заказами + по запросу за покупателем на каждый
for order in Order.objects.all():
    print(order.customer.name)

# 1 запрос с JOIN
for order in Order.objects.select_related("customer"):
    print(order.customer.name)

# 2 запроса: заказы и все их позиции
orders = Order.objects.prefetch_related("items")

Что хотят услышать

  • Что такое N+1 и откуда.
  • select_related и prefetch_related — когда какой.
  • Как обнаружить и не допустить снова.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем