Производительность и оптимизация
В этом документе представлен обзор методов и инструментов, которые помогут сделать код Django более эффективным — быстрее и с меньшим использованием системных ресурсов.
Введение
Обычно в первую очередь необходимо написать код, который работает, чья логика функционирует должным образом, чтобы получить ожидаемый результат. Однако иногда этого недостаточно, чтобы код работал эффективно, как хотелось бы.
В этом случае требуется что-то — а на практике, часто целая совокупность вещей — для повышения производительности кода без или с минимальным влиянием на его поведение.
Общие подходы
Для чего вы оптимизируете?
Важно чётко представлять, что вы подразумеваете под «производительностью». Нет единого показателя производительности.
Повышение скорости может быть самой очевидной целью программы, но иногда могут быть и другие улучшения производительности, такие как меньшее потребление памяти или меньшая нагрузка на базу данных или сеть.
Улучшения в одной области часто приводят к улучшению производительности в другой, но не всегда; иногда одно может даже происходить в ущерб другому. Например, улучшение скорости программы может привести к увеличению потребления памяти. Хуже того, это может быть контрпродуктивно — если улучшение скорости настолько требует памяти, что система начинает испытывать нехватку памяти, вы нанесёте больше вреда, чем пользы.
Есть и другие компромиссы, которые следует учитывать. Ваше собственное время является ценным ресурсом, более ценным, чем время процессора. Некоторые улучшения могут быть слишком сложными, чтобы стоило их реализовывать, или могут повлиять на переносимость или поддерживаемость кода. Не все улучшения производительности стоят усилий.
Итак, вам нужно знать, какие улучшения производительности вы хотите получить, и вам также нужно знать, что у вас есть веская причина для этого стремления — и для этого вам нужно:
Измерение производительности
Не стоит просто гадать или предполагать, где лежат недостатки производительности в вашем коде.
Инструменты Django
django-debug-toolbar — очень удобный инструмент, который даёт представление о том, что делает ваш код и сколько времени он тратит на выполнение. В частности, он может показать все SQL-запросы, которые генерирует ваша страница, и сколько времени потребовал каждый из них.
Для плагина также доступны панели сторонних разработчиков, которые могут (например) сообщать о производительности кэша и времени рендеринга шаблонов.
Сервисы сторонних разработчиков
Существует ряд бесплатных сервисов, которые анализируют и сообщают о производительности страниц вашего сайта с точки зрения удалённого HTTP-клиента, по сути, имитируя опыт реального пользователя.
Они не могут сообщать о внутренней работе вашего кода, но могут дать полезную информацию о общей производительности вашего сайта, включая аспекты, которые невозможно адекватно измерить внутри среды Django. Примеры включают:
Также существуют несколько платных сервисов, которые выполняют аналогичный анализ, включая некоторые, которые учитывают Django и могут интегрироваться с вашим кодом, чтобы профилировать его производительность гораздо более комплексно.
Сделайте всё правильно с самого начала
Некоторые работы по оптимизации включают устранение недостатков производительности, но некоторые из них могут быть включены в то, что вы обычно делаете, как часть хороших практик, которые вы должны применять даже до того, как начнете думать об улучшении производительности.
В этом отношении Python — отличный язык для работы, потому что решения, которые выглядят элегантно и кажутся правильными, обычно являются самыми эффективными. Как и в большинстве навыков, освоение того, что «выглядит правильно», требует практики, но одним из наиболее полезных руководств является:
Работайте на соответствующем уровне
Django предлагает множество различных способов решения задач, но то, что что-то можно сделать определённым способом, не означает, что это самый подходящий способ. Например, вы можете обнаружить, что можете вычислить одно и то же — количество элементов в коллекции, возможно — в QuerySet, на Python или в шаблоне.
Однако почти всегда быстрее выполнить эту работу на более низком, а не на более высоком уровне. На более высоких уровнях система должна обрабатывать объекты через несколько уровней абстракции и слоёв механизмов.
То есть, база данных, как правило, может выполнять операции быстрее, чем Python, который может выполнять их быстрее, чем язык шаблонов:
# QuerySet operation on the database # fast, because that's what databases are good at my_bicycles.count() # counting Python objects # slower, because it requires a database query anyway, and processing # of the Python objects len(my_bicycles)
<!--
Django template filter
slower still, because it will have to count them in Python anyway,
and because of template language overheads
-->
{{ my_bicycles|length }}
В общем случае, наиболее подходящий уровень для задачи — это самый низкий уровень, для которого комфортно писать код.
Примечание
Приведённый выше пример является лишь иллюстративным.
Во-первых, в реальном случае вам необходимо учитывать происходящее до и после подсчёта, чтобы определить, какой способ оптимален в данном конкретном контексте. В документации по оптимизации базы данных описан случай, когда подсчёт в шаблоне будет лучше.
Во-вторых, есть и другие варианты, которые следует учитывать: в реальном случае, {{
my_bicycles.count }}, который вызывает QuerySet count() метод напрямую из шаблона, может быть наиболее подходящим выбором.
Кэширование
Зачастую вычисление значения является ресурсоёмкой и медленной операцией, поэтому очень полезно сохранить значение в быстродоступном кэше, готовом для использования в следующий раз.
Это достаточно значимая и мощная техника, поэтому Django включает в себя полную систему кэширования, а также другие небольшие компоненты кэширования.
Система кэширования
Система кэширования Django предлагает значительные возможности для повышения производительности, сохраняя динамическое содержимое, чтобы оно не нужно было вычислять для каждого запроса.
Для удобства Django предлагает различные уровни кэширования: вы можете кэшировать вывод конкретных представлений или только части, которые трудно произвести, или даже весь сайт.
Реализация кэширования не должна рассматриваться как альтернатива улучшению кода, который плохо работает из-за того, что он был написан плохо. Это один из последних шагов по созданию эффективного кода, а не обходной путь.
cached_property
Часто бывает необходимо вызывать метод экземпляра класса более одного раза. Если эта функция ресурсоёмкая, то делать это может быть неэффективно.
Использование декоратора cached_property сохраняет значение, возвращаемое свойством; в следующий раз, когда функция вызывается для этого экземпляра, она вернёт сохранённое значение, а не пересчитает его. Обратите внимание, что это работает только для методов, которые принимают self в качестве единственного аргумента, и что это изменяет метод на свойство.
Некоторые компоненты Django также имеют собственную функциональность кэширования; они обсуждаются ниже в разделах, посвящённых этим компонентам.
Понимание лени
Ленивость — это стратегия, дополняющая кэширование. Кэширование предотвращает повторное вычисление путём сохранения результатов; ленивость откладывает вычисление до тех пор, пока оно не потребуется.
Ленивость позволяет ссылаться на вещи до их создания или даже до того, как их можно будет создать. Это имеет много применений.
Например, ленивое переложение можно использовать до тех пор, пока целевой язык неизвестен, поскольку оно не выполняется до тех пор, пока переведённая строка фактически не потребуется, например, в рендерном шаблоне.
Ленивость также является способом сэкономить усилия, пытаясь избежать работы в первую очередь. То есть, один из аспектов ленивости — не делать ничего, пока это не нужно, потому что это может оказаться не необходимым. Ленивость, следовательно, может иметь последствия для производительности, и чем сложнее задача, тем больше можно выиграть, используя ленивость.
Python предоставляет ряд инструментов для ленивой оценки, в частности, через конструкции генератор и генераторное выражение. Стоит изучить ленивость в Python, чтобы обнаружить возможности использования ленивых шаблонов в своём коде.
Ленивость в Django
Django сам по себе достаточно ленив. Хороший пример можно найти в оценке QuerySets. Объекты QuerySet ленивы. Таким образом, можно создать QuerySet, передавать его и комбинировать с другими QuerySets, не производя фактических обращений к базе данных для получения описываемых элементов. Передаваемым объектом является QuerySet объект, а не коллекция элементов, которые — в конечном итоге — потребуются из базы данных.
С другой стороны, некоторые операции приведут к принудительной оценке QuerySet. Избегание преждевременной оценки QuerySet может сэкономить дорогостоящее и ненужное обращение к базе данных.
Django также предлагает декоратор keep_lazy(). Это позволяет функции, которая была вызвана с ленивым аргументом, вести себя лениво, вычисляясь только тогда, когда это необходимо. Таким образом, ленивый аргумент — который может быть ресурсоёмким — не будет вызываться для вычисления до тех пор, пока это не будет строго необходимо.
Базы данных
Оптимизация базы данных
В слое баз данных Django предоставляются различные инструменты, помогающие разработчикам добиться наилучшей производительности баз данных. Документация по оптимизации базы данных содержит ссылки на соответствующую документацию и добавляет различные советы, которые описывают шаги, которые следует предпринять при попытке оптимизировать использование вашей базы данных.
Дополнительные советы по работе с базой данных
Это особенно полезно на виртуализированных хостах с ограниченной производительностью сети, например.
Производительность HTTP
Средства промежуточного слоя
Django поставляется с несколькими полезными средствами промежуточного слоя middleware, которые могут помочь оптимизировать производительность вашего сайта. К ним относятся:
ConditionalGetMiddleware
Добавляет поддержку современным браузерам для условного получения ответов на основе заголовков ETag и Last-Modified. Также, при необходимости, рассчитывает и устанавливает ETag.
GZipMiddleware
Сжимает ответы для всех современных браузеров, экономя пропускную способность и время передачи. Обратите внимание, что GZipMiddleware в настоящее время считается риском для безопасности и уязвим для атак, которые сводят на нет защиту, предоставляемую TLS/SSL. См. предупреждение в GZipMiddleware для получения дополнительной информации.
Сессии
Использование кэшированных сессий
Использование кэшированных сессий может быть способом повышения производительности, исключая необходимость загрузки данных сессии из медленного источника хранения, например, базы данных, и вместо этого храня часто используемые данные сессии в памяти.
Статические файлы
Статические файлы, по определению нединамичные, являются отличной целью для оптимизации.
ManifestStaticFilesStorage
Используя возможности кэширования веб-браузеров, вы можете полностью исключить запросы к сети для данного файла после первоначальной загрузки.
ManifestStaticFilesStorage добавляет зависимый от содержимого тег к именам файлов статических файлов, чтобы браузеры могли кэшировать их в долгосрочной перспективе без пропуска будущих изменений. При изменении файла изменяется и тег, поэтому браузеры автоматически перегрузят ресурс.
“Минификация”
Некоторые сторонние инструменты и пакеты Django обеспечивают возможность «минификации» HTML, CSS и JavaScript. Они удаляют лишние пробелы, новые строки и комментарии, а также сокращают имена переменных, тем самым уменьшая размер документов, публикуемых вашим сайтом.
Производительность шаблонов
Обратите внимание, что:
- использование
{% block %}быстрее, чем использование{% include %} - сильно фрагментированные шаблоны, собранные из множества мелких фрагментов, могут влиять на производительность
Кэширующий загрузчик шаблонов
Включение cached template loader часто значительно улучшает производительность, поскольку это позволяет избежать компиляции каждого шаблона каждый раз, когда он должен быть рендерирован.
Использование различных версий доступного программного обеспечения
Иногда стоит проверить, есть ли доступные различные и более производительные версии используемого программного обеспечения.
Эти методы предназначены для более опытных пользователей, которые хотят расширить пределы производительности уже хорошо оптимизированного сайта Django.
Однако это не магические решения проблем с производительностью, и вряд ли они принесут больше чем незначительные улучшения сайтам, которые не выполняют более простые задачи должным образом.
Примечание
Стоит повторить: поиск альтернатив используемому программному обеспечению — никогда не первое решение проблем с производительностью. На этом уровне оптимизации вам необходимо формальное решение для бенчмаркинга.
Новое часто, но не всегда, лучше
Вполне редко, чтобы новая версия хорошо поддерживаемого программного обеспечения была менее эффективной, но разработчики не могут предвидеть все возможные варианты использования. Поэтому, хотя следует понимать, что новые версии, скорее всего, будут работать лучше, не предполагайте, что это всегда так.
Это справедливо и для Django. Последовательные выпуски предлагают ряд улучшений в системе, но вам все равно следует проверять реальную производительность вашего приложения, потому что в некоторых случаях вы можете обнаружить, что изменения ухудшают ее, а не улучшают.
Новые версии Python, а также пакетов Python, часто работают лучше, но измеряйте, а не предполагайте.
Примечание
Если вы не столкнулись с необычной проблемой производительности в определенной версии, обычно вы найдете лучшие функции, надежность и безопасность в новой версии, и эти преимущества намного значительнее, чем любая производительность, которую вы можете выиграть или потерять.
Альтернативы языку шаблонов Django
Практически во всех случаях встроенный язык шаблонов Django вполне достаточен. Однако, если узкие места в вашем проекте Django кажутся связанными с системой шаблонов, и вы исчерпали другие возможности исправить это, сторонняя альтернатива может быть решением.
Jinja2 может предложить улучшения производительности, особенно в отношении скорости.
Альтернативные системы шаблонов различаются по степени, в которой они используют язык шаблонов Django.
Примечание
Если у вас возникнут проблемы с производительностью в шаблонах, самое первое, что нужно сделать, это понять, почему. Использование альтернативной системы шаблонов может оказаться быстрее, но те же улучшения также могут быть доступны без особых усилий, например, сложная обработка и логика в ваших шаблонах может выполняться более эффективно в ваших представлениях.
Альтернативные реализации программного обеспечения
Может быть полезно проверить, доступно ли программное обеспечение Python, которое вы используете, в другой реализации, которая может выполнять тот же код быстрее.
Однако: большинство проблем с производительностью на хорошо написанных сайтах Django связаны не с уровнем выполнения Python, а с неэффективными запросами к базе данных, кешированием и шаблонами. Если вы полагаетесь на плохо написанный код Python, проблемы с производительностью вряд ли решатся за счет ускорения его выполнения.
Использование альтернативной реализации может привести к проблемам совместимости, развертывания, переносимости или обслуживания. Само собой разумеется, что перед принятием нестандартной реализации вы должны убедиться, что она обеспечивает достаточные улучшения производительности для вашего приложения, чтобы перевесить потенциальные риски.
С учетом этих оговорок вы должны знать о:
PyPy
PyPy — это реализация Python на Python (стандартная реализация Python написана на C). PyPy может обеспечить значительное ускорение, особенно для многозадачных приложений.
Ключевая цель проекта PyPy — совместимость с существующими API и библиотеками Python. Django совместим, но вам нужно проверить совместимость других используемых библиотек.
Реализации библиотек Python на C
Некоторые библиотеки Python также реализованы на C и могут быть намного быстрее. Они стремятся предложить те же самые API. Обратите внимание, что проблемы совместимости и различия в поведении не исключены (и не всегда сразу очевидны).
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/topics/performance/