Django: кеширование шаблонов помогло увеличить пропускную способность сайта

Джулия Эванс описывает, как учится делать многостраничные сайты в подходе, характерном для 2010-х: с SQL-базой и HTML, который рендерится на сервере. Раньше она в основном работала со статическими сайтами, небольшими одностраничными приложениями на Vue.js и бэкендами на Lambda или Go. Для проекта со множеством страниц ей захотелось сократить объём клиентского JavaScript и держать больше логики в одном месте; Django оказался для этого удобнее, чем попытки использовать стандартную библиотеку Go или Flask.

Среди полезных возможностей Django она выделяет настраиваемые QuerySet: методы с условиями WHERE можно объединять в читаемые цепочки, например для отбора одобренных событий, вкладки, фестивалей, бесплатных или уличных мероприятий. Ей также нравятся фильтры шаблонов: urlize и linebreaksbr для преобразования URL и переводов строк, date для форматирования дат, json_script для безопасной вставки Python-словаря как JSON в HTML и querystring для изменения или удаления одного параметра в ссылке. Автоматические миграции базы данных, по её словам, особенно полезны, когда модель данных меняется вместе с пониманием задачи: в проекте уже сделано 19 миграций.

При этом Эванс не понравилось использовать наследование и классовые представления для общего кода нескольких страниц: она попробовала этот подход и перешла к функциям. Наследование для интерфейсов, которые предоставляет сам Django, например при определении QuerySet, она считает приемлемым.

Отдельная часть заметки посвящена производительности. После того как сканеры, связанные с языковыми моделями, стали отправлять к сайту примерно 10 запросов в секунду, автор заблокировала их и задумалась о запасе производительности. Лёгкий тест ab с 1000 запросами и одним одновременным соединением показывал примерно 2, 3 запроса в секунду на виртуальной машине стоимостью около 10 долларов в месяц. Изучив документацию Django, она обнаружила, что ранее случайно отключила кешированный загрузчик шаблонов, хотя он должен быть включён по умолчанию. После включения кеширования шаблонов сайт, по её наблюдению, начал без полной загрузки CPU справляться примерно с 12 запросами в секунду; тщательного сравнения до и после она не проводила.

Этот случай изменил её взгляд на поиск узких мест: совет сначала проверять запросы к базе и индексы не всегда объясняет проблему. В её проекте значительную роль сыграла компиляция шаблонов, а начать разбор оказалось полезнее с профилирования CPU. Автор также отмечает, что пока не решила, нужно ли проектировать сайт под всплески трафика и масштабирование, активнее использовать кеши, менять систему шаблонов на Jinja или выяснять разницу между block и include.

Ключевые факты

  • Эванс использует Django для многостраничного серверного сайта с SQL-базой и стремится свести JavaScript к минимуму.
  • Настраиваемые QuerySet позволяют собрать часто используемые условия WHERE в читаемые методы, которые можно объединять в цепочки.
  • В проекте уже создано 19 миграций; автор ценит, что Django генерирует их после изменения моделей.
  • При лёгком тесте сайт выдавал около 2, 3 запросов в секунду на виртуальной машине примерно за 10 долларов в месяц, а после включения кеширования шаблонов, около 12 запросов в секунду без полной загрузки CPU.
  • Автор не считает медленные запросы к базе единственной вероятной причиной проблем с производительностью и советует начать с профилирования CPU.

Почему это важно

Заметка показывает практическую причину медленной работы Django-сайта, не связанную с базой данных: повторную компиляцию шаблонов. Случайно отключённый кешированный загрузчик шаблонов заметно снизил пропускную способность небольшого сайта, а включение настройки, по наблюдению автора, дало существенный эффект.

Кому это важно

Материал полезен разработчикам, которые переходят от статических сайтов или фронтенд-ориентированных приложений к многостраничному серверному приложению на Django. Он также пригодится тем, кто настраивает небольшой Django-сайт и ищет причины ограниченной производительности.

Как это применить

Для повторяющихся фильтров данных можно вынести условия в методы собственного QuerySet и собирать их в цепочки. В шаблонах стоит использовать встроенные фильтры для URL, дат, безопасной передачи JSON и параметров запроса. При жалобах на скорость следует проверить настройки кешированного загрузчика шаблонов и снять профиль CPU, прежде чем предполагать, что проблема обязательно в SQL-запросах.

Можно ли доверять

Это личный опыт автора, а не строгий бенчмарк: она прямо пишет, что не проводила тщательное сравнение производительности до и после включения кеша. Конкретные показатели относятся к её сайту, её нагрузочному тесту и виртуальной машине примерно за 10 долларов в месяц, но описание отключённой настройки и последующего наблюдения прозрачно отделено от общих выводов.

Риски и подводные камни

Нельзя переносить показатель около 12 запросов в секунду на другой Django-проект: результат зависит от шаблонов, данных, оборудования и характера нагрузки. Автор также пока не знает, как лучше подготовиться к кратковременным всплескам трафика, нужно ли масштабирование и насколько оправданы дополнительные кеши или смена шаблонизатора. Кеширование требует аккуратной настройки, а изменения в settings.py легко отключают важные значения по умолчанию.

«Включение кешированного загрузчика шаблонов часто резко повышает производительность, поскольку не нужно компилировать каждый шаблон при каждом рендеринге.»

— документация Django, цитата в заметке Джулии Эванс