Стоит ли использовать django в 2024? Я думаю — да. DRF очень удобен, скорость разработки очень высока (особенно, если использовать generic views, django‑filters), огромное количество готовых батареек сильно облегчает жизнь и встроенная админка хорошо подходит для большинства сайтов. Полностью асинхронные фреймворки (или переход на другой язык) не дадут большого выиграша, если ваш сервис много работает в БД — вы упретесь в её производительность и ограничения количества соединений с пулом бд. Далее я вкратце пробегусь по основным моментам и дам ссылки на документацию и готовые батарейки.
Django проекты могут начать тормозить с ростом нагрузки. Как правило, это связанно с ростом нагрузки на БД и проблем тут может быть несколько:
N+1
При работе со связанными таблицами необходимо явно указывать их при составлении запроса с помощью select_related для foreign key и prefect_related для many to many. Иначе для загрузки одой страницы у вас буду десятки или сотни запросов. Чтобы понять что это происходит можно использовать silk. Эта библиотека логгирует все запросы которые происходят при загрузки страницы, можно посмотреть сколько их было, сколько времени они выполнялись и план (explain) каждого запроса. (такой функционал есть и в django‑debug‑toolbar, но это не очень удобно, если у вас, например, мобильное приложение с бэкендом на drf). Если запросов на страницу много скорее всего вы забыли сделать select/prefetch. Вот пример очень печальной ситуации:
А вот те же страницы после добавления select/prefetch related:
Если вам необходимо дополнительно фильтровать many to many таблицы — нужно использовать prefech объекты. Например, вы хотите сделать что-то такое:
book = Book.objects.get(pk=1)
shops = book.shop_set.filter(city_id=1)
Это можно сделать так:
book = Book.objects.prefetch_related(Prefetch("shop_set", queryset=Shop.objects.filter(city_id=1), to_attr="city_shop_set")).get(pk=1)
shops = book.city_shop_set.all()
Это возможно делать и автоматически или так.
Индексы
Во вкладке SQL можно посмотреть какие запросы долго выполнялись и их план. Возможно, необходимо создать какие-то индексы. (обратите внимание если там есть full scan).
Также, можно также явно указать какие колонки вам нужны с помощью only/defer чтобы не гонять лишние данные.
Некоторые запросы могут долго выполняться из-за каких-то аггреций, например, медленных count. Можно кешировать их или хранить их значения в самой таблице и переодически или по сигналам обновлять. Или требуется какая-то денормализация, чтобы избежать subquery.
silk также позволяет профилировать python код ваших view:
Репликация
Django поддерживает репликацию бд. С ростом нагрузки имеет смысл направлять select запросы, которых, как правило, намного больше в read-only реплики. Если ваш проект имеет обширую географию можно также использовать шардинг или tenants. Для django также есть готовые батарейки.
Рекурсия
Рекурсивные запросы к БД можно написать помощью common table expressions. Или, если это какие-то деревья использовать MPTT.
Асинхронные view
Обычно какие-то запросы к внешним сервисам делают внутри celery, но современные версии django поддерживают асинхронные view. Стоит обратить на них внимание. Для асинхронных http запросов можно использовать aiohttp или httpx внутри таких view.
Сериализаторы
Постарайтесь избегатть to_representation и SerializerMethodField и, особенно, делать какие-то запросы внутри него.
Кеширование
Обычно, можно безболезненно кешировать многие страницы, по крайней мере, на какое-то коротное время, не стоит забывать инвалидировать кэш и при кешировании страницы обращать внимание на куки или auth хедеры, если она различается для разных пользователй. Запросы к БД тожн можно кешировать в т.ч. автоматически.
graphql
Вместо того, чтобы делать много rest запросов на страницу, чтобы получить всякие вложенные данные, можно перейти на graphql и запрашивать их все сразу.
Materialized views
Django вполне может работать и с ними. Достаточно описать такое view в виде модели с managed = False.
Ну и, наконец, какие-то в принципе не очень хорошо делаются в реляционеных БД. Например, если вам требуется фасетный поиск, стоит обратить внимание на elastic/open search.
Комментарии (9)
Andrey_Solomatin
20.07.2024 04:14С graphql не работал, но слышал что там не всё так просто. Поделитесь опытом как его использование отразилось на производительности.
pawnhearts Автор
20.07.2024 04:14Производительность я не сравнивал, думаю не будет сильно отличаться от аналогичного rest api если там nestedserializers. Там можно использовать как сериализаторы, так всякие ObjectType из графена, это может отличаться. Это довольно инопланетянская штука при использовании с django и я не очень умею её готовить. Особенность в том, что фронту не надо просить менять что-то на каждый чих и может запрашивать разные наборы данных, включая связанные модели(в пределах описанной схемы), и это может привести или к лишним запросам или к N+1, но, наверное можно как-то по-умному составлять динамически запрос к БД, в зависимости от graphql запроса, но там где я его использовал был ограниченный набор запросов. И "мутации" писать мне не очень понравилось, хотя там можно создавать/обновлять сразу много объектов тоже в одном запросе.
Думаю сильно зависит от особенностей самого проекта, где-то это ложится хорошо, где-то overkill.
titan_pc
20.07.2024 04:14Я думал будет что-то вроде. Берём Джанго. Выкидываем Джанго. Пишем запросы руками - счастье радость восторг овации.
Но когда в руках молоток - весь мир гвоздь. Можно и на django sql оптимизировать ради "скорости разработки".
hardtop
20.07.2024 04:14Джанга вполне себе инструмент. Да, надо помнить об оптимизации, а где не надо?
Что предлагаете? Фастапи? Старлайт? Фласк?
pawnhearts Автор
20.07.2024 04:14Пишем запросы руками.. И лишаемся автоматических миграций(да, это можно и без orm сделать, но тем не менее), конструктора запросов - теперь когда у нас разный запрос в зависимости от параметров запроса, например, разные фильтры - мы будем лепить это его в виде строки. Всякие валидаторы и сериализаторы будем изобретать заново. В итоге это будет больше кода и потенциально больше багов и уязвимостей. То что на django делается в несколько строк - например какой-то viewset с несколькими фильтрами будет сотни строк кода и десяток разных запросов руками.
В общем, орм придумали не просто так. Да, есть проекты, где без них вполне можно обойтись, но для всякого CRUD они сильно облегчают жизнь.
titan_pc
20.07.2024 04:14Опен-сорс это помойка процентов на 90. И ORM - это как раз то ещё хламище. Если сил не хватает свой генератор запросов сделать - ну одна дорога да в ОРМ. Он прекрасен...
Andrey_Solomatin
20.07.2024 04:14ORM нормальный инструмент для своего круга задач. Быстрый старт, дешёвая поддержка. С производительностью и сложными запросами могут быть проблемы.
Один инструмент не будет эффективен на всех проектах, не надо везде строить свои велосипеды.
nikolz
Поправьте заголовок, например : Оптимизация запросов к БД на django