Производительность и оптимизация¶
В этом документе представлен обзор техник и инструментов, которые могут помочь сделать ваш код 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 }}
Как правило, наиболее подходящим для работы является самый низкий уровень, на котором удобно кодировать.
Примечание
Приведенный выше пример является лишь иллюстративным.
Во-первых, в реальном случае вам нужно рассмотреть, что происходит до и после вашего подсчета, чтобы понять, какой способ оптимален в данном конкретном контексте. В документах по оптимизации баз данных описывается a case where counting in the template would be better.
Во-вторых, есть и другие варианты: в реальном случае наиболее подходящим выбором может быть {{ my_bicycles.count }}
, который вызывает метод QuerySet
count()
непосредственно из шаблона.
Кэширование¶
Часто вычисление значения является дорогостоящим (то есть ресурсоемким и медленным), поэтому сохранение значения в быстродоступном кэше, готовом к следующему разу, когда оно потребуется, может принести огромную пользу.
Это достаточно значимая и мощная техника, поэтому Django включает в себя комплексный фреймворк кэширования, а также другие более мелкие части функциональности кэширования.
The caching framework¶
Django caching framework предлагает очень значительные возможности для повышения производительности, сохраняя динамическое содержимое так, что его не нужно вычислять для каждого запроса.
Для удобства Django предлагает различные уровни детализации кэша: вы можете кэшировать вывод конкретных представлений, или только те части, которые трудно производить, или даже весь сайт.
Реализация кэширования не должна рассматриваться как альтернатива улучшению кода, который работает плохо, потому что был плохо написан. Это один из последних шагов на пути к созданию хорошо работающего кода, а не короткий путь.
cached_property
¶
Часто бывает, что метод экземпляра класса приходится вызывать более одного раза. Если эта функция дорогая, то это может быть расточительно.
Использование декоратора cached_property
сохраняет значение, возвращаемое свойством; при следующем вызове функции на этом экземпляре она вернет сохраненное значение, а не будет вычислять его заново. Обратите внимание, что это работает только для методов, которые принимают self
в качестве единственного аргумента, и что это меняет метод на свойство.
Некоторые компоненты Django также имеют свои собственные функции кэширования; они обсуждаются ниже в разделах, связанных с этими компонентами.
Понимание лени¶
*Лень» - это стратегия, дополняющая кэширование. Кэширование позволяет избежать повторных вычислений, сохраняя результаты; лень откладывает вычисления до тех пор, пока они действительно не потребуются.
Лень позволяет нам ссылаться на вещи до того, как они будут инстанцированы, или даже до того, как их можно будет инстанцировать. Это имеет множество применений.
Например, lazy translation может быть использовано еще до того, как будет известен целевой язык, поскольку оно не происходит до тех пор, пока переведенная строка действительно не потребуется, например, в отрисованном шаблоне.
Лень - это также способ сэкономить усилия, пытаясь избежать работы в первую очередь. То есть, один из аспектов лени - не делать ничего до тех пор, пока это не нужно делать, потому что это может оказаться ненужным в конце концов. Поэтому лень может иметь последствия для производительности, и чем дороже работа, тем больше выгоды можно получить от лени.
Python предоставляет ряд инструментов для ленивой оценки, в частности, конструкции generator и generator expression. Стоит почитать о лени в Python, чтобы найти возможности для использования ленивых шаблонов в вашем коде.
Лень в Django¶
Django сам по себе довольно ленив. Хороший пример этого можно найти в оценке QuerySets
. QuerySets are lazy. Таким образом, объект QuerySet
можно создавать, передавать и комбинировать с другими QuerySets
, не совершая никаких обращений к базе данных для получения элементов, которые он описывает. Передается только объект QuerySet
, а не коллекция элементов, которые - в конечном итоге - потребуются из базы данных.
С другой стороны, certain operations will force the evaluation of a QuerySet. Избегая преждевременной оценки QuerySet
, можно избежать дорогостоящего и ненужного обращения к базе данных.
Django также предлагает декоратор keep_lazy()
. Это позволяет функции, которая была вызвана с ленивым аргументом, вести себя лениво, оценивая его только тогда, когда это необходимо. Таким образом, ленивый аргумент - который может быть дорогим - не будет вызываться для оценки до тех пор, пока это не будет строго необходимо.
Базы данных¶
Оптимизация базы данных¶
Уровень баз данных Django предоставляет различные способы помочь разработчикам добиться максимальной производительности от своих баз данных. В database optimization documentation собраны ссылки на соответствующую документацию и добавлены различные советы, которые описывают шаги, которые необходимо предпринять при попытке оптимизировать использование базы данных.
Производительность HTTP¶
Middleware¶
Django поставляется с несколькими полезными элементами middleware, которые могут помочь оптимизировать производительность вашего сайта. К ним относятся:
ConditionalGetMiddleware
¶
Добавляет поддержку современных браузеров для условного GET-ответа на основе заголовков ETag
и Last-Modified
. Он также вычисляет и устанавливает ETag, если это необходимо.
GZipMiddleware
¶
Сжимает ответы для всех современных браузеров, экономя пропускную способность и время передачи. Обратите внимание, что GZipMiddleware в настоящее время считается риском для безопасности и уязвима к атакам, которые сводят на нет защиту, обеспечиваемую TLS/SSL. Для получения дополнительной информации см. предупреждение в GZipMiddleware
.
Сессии¶
Использование кэшированных сессий¶
Using cached sessions может быть способом повышения производительности за счет устранения необходимости загрузки данных сеанса из более медленного источника хранения, такого как база данных, и вместо этого хранения часто используемых данных сеанса в памяти.
Статические файлы¶
Статические файлы, которые по определению не являются динамическими, представляют собой отличную цель для оптимизации.
ManifestStaticFilesStorage
¶
Используя возможности кэширования веб-браузеров, вы можете полностью исключить сетевые запросы для данного файла после его первоначальной загрузки.
ManifestStaticFilesStorage
добавляет к именам файлов static files тег, зависящий от содержания, чтобы браузеры могли кэшировать их длительное время, не пропуская будущих изменений - когда файл меняется, меняется и тег, поэтому браузеры автоматически перезагружают актив.
«Минификация»¶
Несколько сторонних инструментов и пакетов 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 является compatibility с существующими API и библиотеками Python. Django совместим, но вам нужно будет проверить совместимость других библиотек, на которые вы полагаетесь.
Реализация библиотек Python на языке C¶
Некоторые библиотеки Python также реализованы на C, и могут быть намного быстрее. Их цель - предложить те же API. Обратите внимание, что проблемы совместимости и различия в поведении небезызвестны (и не всегда сразу очевидны).