Производительность и оптимизация
В данном документе представлен обзор техник и инструментов, которые помогут сделать ваш код 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 поставляется с несколькими полезными средствами средств, которые могут помочь оптимизировать производительность вашего сайта. Они включают:
ConditionalGetMiddleware
Добавляет поддержку современным браузерам условного получения ответов на основе заголовков ETag и Last-Modified. Также вычисляет и устанавливает тег ETag при необходимости.
GZipMiddleware
Сжимает ответы для всех современных браузеров, экономя пропускную способность и время передачи. Обратите внимание, что GZipMiddleware в настоящее время считается риском для безопасности и уязвим для атак, которые сводят на нет защиту, предоставляемую TLS/SSL. См. предупреждение в GZipMiddleware для получения дополнительной информации.
Сессии
Использование кэшированных сессий
Использование кэшированных сессий может быть способом повышения производительности, устраняя необходимость загрузки данных сессии из более медленного источника хранения, например, базы данных, и вместо этого храня часто используемые данные сессии в памяти.
Статические файлы
Статические файлы, по определению являющиеся нединамическими, являются отличной целью для оптимизации.
CachedStaticFilesStorage
Используя возможности кэширования веб-браузеров, вы можете полностью устранить сетевые запросы для данного файла после первоначальной загрузки.
CachedStaticFilesStorage добавляет зависимый от содержимого тег в имена файлов статических файлов, чтобы браузеры могли кэшировать их долгосрочно, не пропуская будущие изменения - при изменении файла тег также изменяется, поэтому браузеры автоматически перезагружают актив.
“Минификация”
Несколько сторонних инструментов и пакетов 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/2.1/topics/performance/