Стоит ли использовать 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.

Комментарии (5)


  1. nikolz
    20.07.2024 04:14

    Введение в оптимизация запросов к БД на django c помощью silk

    Поправьте заголовок, например : Оптимизация запросов к БД на django


  1. Andrey_Solomatin
    20.07.2024 04:14

    С graphql не работал, но слышал что там не всё так просто. Поделитесь опытом как его использование отразилось на производительности.


    1. pawnhearts Автор
      20.07.2024 04:14

      Производительность я не сравнивал, думаю не будет сильно отличаться от аналогичного rest api если там nestedserializers. Там можно использовать как сериализаторы, так всякие ObjectType из графена, это может отличаться. Это довольно инопланетянская штука при использовании с django и я не очень умею её готовить. Особенность в том, что фронту не надо просить менять что-то на каждый чих и может запрашивать разные наборы данных, включая связанные модели(в пределах описанной схемы), и это может привести или к лишним запросам или к N+1, но, наверное можно как-то по-умному составлять динамически запрос к БД, в зависимости от graphql запроса, но там где я его использовал был ограниченный набор запросов. И "мутации" писать мне не очень понравилось, хотя там можно создавать/обновлять сразу много объектов тоже в одном запросе.

      Думаю сильно зависит от особенностей самого проекта, где-то это ложится хорошо, где-то overkill.


  1. titan_pc
    20.07.2024 04:14

    Я думал будет что-то вроде. Берём Джанго. Выкидываем Джанго. Пишем запросы руками - счастье радость восторг овации.

    Но когда в руках молоток - весь мир гвоздь. Можно и на django sql оптимизировать ради "скорости разработки".


    1. hardtop
      20.07.2024 04:14

      Джанга вполне себе инструмент. Да, надо помнить об оптимизации, а где не надо?

      Что предлагаете? Фастапи? Старлайт? Фласк?