Производительность и оптимизация
В этом документе представлен обзор техник и инструментов, которые помогут сделать ваш код 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. QuerySets ленивые. Таким образом, можно создать 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/1.11/topics/performance/