Сегодня я буду писать о Django — фреймворке, который верно служит мне на протяжении последних пяти лет. Он помог мне преуспеть в разработке высоконагруженных решений, используемых сегодня миллионами пользователей. Действительно, Python не очень «быстрый» язык программирования, однако он прост, удобен и люди его любят. С точки зрения производительности, он не может быть таким же быстрым, как Go или Node.js, но это становится несущественным, если рассматривать современные инфраструктуры и модульную разработку. Поскольку я уже несколько лет варюсь в этом «котле разработки на Django», я пришел к нескольким ценным выводам, которыми собираюсь с вами поделиться. Помимо производительности приложения, первое, что вам нужно — это инфраструктура, которая позволяет вам осуществлять масштабирование, когда приложение достигает своего предела, и Django может легко масштабироваться, если следовать следующим правилам: Какое бы ускорение выполнения кода вы не получили, скорее всего оно будет потеряно на стороне базы данных. А именно, скорость ответа конечной точки обработки запросов зависит от того, насколько быстро обрабатывается запрос к базе данных, следовательно, следует проверить следующее: При разработке чрезвычайно важно следить за тем, какие запросы генерирует ORM, а также за скоростью ответа. Когда вы создаете конечную точку обработки запросов, вы должны убедиться, что время ее ответа менее 100 миллисекунд — именно поэтому запросы должны выполняться не дольше 20 миллисекунд.  Чтобы включить логи и увидеть, за какое время выполняется каждый запрос, используйте следующие строки кода в settings.py: И после перезапуска вы должны увидеть запросы в таком формате:  Первое число представляет собой время выполнения запроса Если ваш выбор — PostgreSQL, то я рекомендую для просмотра медленных запросов и повторяющихся индексов использовать панель мониторинга производительности pghero. Если приложению нужно обрабатывать большое количество запросов, включите поддержку постоянных соединений с базой данных. По умолчанию Django закрывает соединение в конце каждого запроса, а постоянные соединения предотвращают подобную нагрузку на базу данных из-за каждого запроса. Эти соединения контролируются параметром CONN_MAX_AGE — показателем, который определяет максимальное время существования соединения. Установите подходящее значение в зависимости от вашего объема запросов к точке обработки запросов приложения. Обычно я ограничиваю это время 5 минутами. Убедитесь, что база данных не ограничена в числе одновременных соединений, которое, как правило, по умолчанию установлено в 100 соединений, а этого чаще всего не достаточно в случае высокой нагрузки. Например, в одном из моих введенных в эксплуатацию проектов после установки этого параметра с 0 до 300 секунд я вдвое уменьшил нагрузку на базу данных. Я воспользовался движком базы данных AWS Aurora с инстансом db.r5.8xlarge, переходя на менее мощный db.r5.4xlarge, чтобы сократить расходы, но, в то же время, обеспечить достаточный уровень производительности.  По умолчанию у фреймворка есть несколько включенных приложений, которые могут быть бесполезны, особенно если вы используете Django как REST API. Обратите внимание на сессии (sessions) и сообщения (messages) — в таком сценарии работы они бесполезны и просто тратили бы ресурсы и уменьшали скорость обработки. Чем меньше промежуточных слоев вы объявили, тем быстрее будет обрабатываться каждый запрос.  Используйте bulk-запросы, чтобы эффективно запрашивать большие наборы данных и уменьшать количество запросов к базе данных. Django ORM может выполнять несколько операций вставки или обновления в одном SQL-запросе. Если вы собираетесь вставлять более 5000 объектов, задайте batch_size (прим. пер.: размер пакета). Большие пакеты также снизят время обработки и высокое потребление памяти в Python, следовательно, вы должны найти оптимальное количество элементов, в зависимости от размера объекта.  Пример bulk-запроса в Django Если у вас есть две связанные модели и нужно извлечь определенные свойства из обеих, то сделайте предварительную выборку записей через оператор JOIN. Вот печальный пример, который иллюстрирует генерацию 11 ненужных запросов к базе данных:  С другой стороны, вот правильный способ, с генерацией только одного запроса:  Использование select_related зависит от размеров таблицы, поскольку ORM генерирует SQL-запрос с JOIN. Чтобы добиться оптимизации, условие в операторе WHERE должно возвращать небольшое количество строк.  Сфокусируйтесь на существенно важной информации из базы данных. Выборка необязательных столбцов увеличивает время ответа от базы данных, приводя к расходам на передачу данных.
В Django ORM у класса QuerySet есть функция .only() для выбора определенных полей, или же вы можете вызвать .defer(), чтобы сообщить Django о том, что некоторые поля из базы данных извлекать не нужно:  Выборка имени и электронной почты из таблицы Подобно строгой выборке из базы данных, очень важно возвращать необходимую информацию из API. Поскольку JSON не самый эффективный способ отправки данных, нужно уменьшать его размер с помощью исключения полей, которые не используются клиентом приложения. Например: размер ответа от определенной конечной точки обработки запросов составляет 1 КБ, но если он вызывается 1 миллион раз в день, то ежедневно будет передаваться 1 ГБ данных, что означает 30 ГБ в месяц — довольно большая цена за использование (прим. пер.: вашего) ресурса. Конечно, легко винить Django или Python, однако, как говорят мои коллеги: «Не вините пианино — вините пианиста». При разработке высоконагруженного проекта на Django важна любая мелочь. Проблемы тоньше волоса, помноженные на миллионы, приводят к весьма ужасному (прим. пер.: или «мохнатому») положению дел, и вам придется заняться «стрижкой» всех этих проблем. Любая лишняя миллисекунда, умноженная на миллионы запросов, может привести к чрезмерному потреблению ресурсов. Если приложение уже оптимизировано или хорошо выстроено, увеличение аппаратных ресурсов не спасет положение. Возьмите пример с Instagram, Pinterest или Disqus — они начали с Django «как есть», и подняли его на следующий уровень. Конечно, это, быть может, уже не тот же самый фреймворк, однако, если в основе лежит здравый смысл, то это только на пользу. Пишите код эффективно и используйте его повторно, пользуйтесь bulk-запросами, делайте мониторинг, замеряйте и оптимизируйте. Скоро увидимся. Marketing Specialist