Если вы читаете эту статью, скорее всего, вы сталкивались с проблемой N+1 во время разработки на Django или хоть раз с ней сталкивались. Из-за этой проблемы скорость загрузки страниц снижается, и вы, вероятно, узнали о select_related
и prefetch_related
в поисках решения.
Вы нашли мой блог! 🎉 Прочитав эту статью до конца, вы точно освоите эффективные способы решения проблемы N+1.
Если вы еще не совсем понимаете концепцию проблемы N+1, я рекомендую сначала прочитать статью ниже.
✅ Что такое проблема N+1 в Django ORM? Почему она возникает?
🔍 Два способа решения проблемы N+1 в Django
В Django ORM есть select_related
и prefetch_related
, чтобы решить проблему N+1. Однако важно точно понимать, при каких обстоятельствах следует использовать каждую из этих функций, поскольку они работают по-разному.
📌 Общий принцип select_related
и prefetch_related
Главный общий принцип этих двух методов состоит в том, что они извлекают необходимые поля из базы данных в первом запросе, чтобы эффективно использовать извлеченные данные внутри Django.
Например, представьте, что вы идете в крупный супермаркет, чтобы испечь торт, и покупаете все необходимые ингредиенты, такие как мука, сливки и фрукты, сразу. Когда вы вернетесь домой и начнете готовить, у вас уже будет готовый набор всех ингредиентов, и вы сможете сразу приступить к приготовлению.
Но что, если вы будете покупать необходимые ингредиенты по одному? Вы купили муку и вернулись, чтобы начать замешивать тесто, но обнаружили, что у вас нет сливок, и вам снова надо идти в супермаркет. Затем вы купили сливки и пытаетесь продолжить работу, но теперь у вас нет фруктов, и вам снова нужно идти в магазин. Что тогда?
В таком формате приготовление займет очень много времени. select_related
и prefetch_related
в Django ORM помогают избежать такого неэффективного извлечения данных, извлекая необходимые данные за один запрос к базе данных, чтобы быстро их обработать.
✅ 1. select_related
– немедленная загрузка с использованием SQL JOIN
Используется в отношениях ForeignKey(1:N)
- Получает данные одним запросом с помощью SQL JOIN
- Так как связанные объекты загружаются сразу, дополнительных запросов к базе данных нет
📌 Пример использования select_related
authors = Author.objects.select_related('post_set').all()
🧐 SQL, выполняемая, когда select_related
не применяется
SELECT * FROM author;
SELECT * FROM post WHERE author_id = 1;
SELECT * FROM post WHERE author_id = 2;
...
🚀 SQL, выполняемая, когда применяется select_related
SELECT * FROM author INNER JOIN post ON author.id = post.author_id;
✅ 2. prefetch_related
– предварительная загрузка с использованием отдельных запросов
Используется в отношениях ManyToMany, Reverse ForeignKey
- Выполняет отдельные запросы, затем Django оптимизирует данные на Python и соединяет их
- Так как SQL JOIN не используется, это может быть более выгодно при обработке больших объемов данных
📌 Пример использования prefetch_related
authors = Author.objects.prefetch_related('post_set').all()
🧐 SQL, выполняемая после применения prefetch_related
SELECT * FROM author;
SELECT * FROM post WHERE author_id IN (1, 2, 3, 4, 5, ...);
🎯 Заключение – вот и всё, чтобы решить проблему N+1!
Если не решить проблему N+1 при использовании Django ORM, это может серьёзно ухудшить производительность. Но, используя select_related
и prefetch_related
правильно, вы сможете минимизировать количество выполненных SQL-запросов и значительно улучшить скорость загрузки страниц.
✅ Резюме
- Если это отношение ForeignKey(1:N), используйте
select_related
- Если это отношение ManyToMany или обратное отношение, используйте
prefetch_related
- В любом случае необходимо проверить выполняемые SQL-запросы и оптимизировать их
- Если скорость выполнения запросов снижается, следует подозревать проблему N+1
📌 Прочитайте также статьи на эту тему!
✅ Что такое проблема N+1 в Django ORM? Почему она возникает?
Теперь попробуйте решить проблему N+1 с помощью select_related
и prefetch_related
! 🚀 Если что-то не совсем понятно или у вас есть дополнительные вопросы, оставьте комментарий или задайте вопрос. 😊
댓글이 없습니다.