High Load WordPress Background
Баннер

Оптимизации WordPress под Высокую Нагрузку и для Скорости Загрузки

В этой секции:

Что считается высокой нагрузкой?

Для некоторых читателей это может быть новая тема, а другие уже могут быть знакомы с ней, поэтому начнем с небольшого введения, чтобы все могли понять суть.

У многих сайтов может быть высокое количество посетителей, но это не обязательно означает, что на сайт создается высокая нагрузка.

Если клиенты не посещают сайт одновременно, а сайт обслуживает 10 000 пользователей ежедневно, то в сутках 24 часа, а это 1440 минут. Если средний пользователь проводит на сайте около минуты, на сайте в среднем будет менее 10 пользователей в минуту, что не является высокой нагрузкой, и многие сайты могут справляться с таким количеством пользователей при помощи стандартного хостинга.

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

Также, если вы активно ведете блог, ваши статьи могут занять высокие позиции в результатах поиска и неожиданно привести к большому количеству посетителей за короткий промежуток времени, но в отличие от рекламной кампании вы не будете знать когда это произойдет.

Кроме того, важно отметить, что каждый пользователь будет отправлять несколько запросов на ваш сервер — посещение страниц, загрузка постраничной навигации и отправка форм — каждое из этих действий является отдельным запросом, как и все сопутствующие ресурсы — картинки, стили и скрипты.

Учитывая это, высокая нагрузка обычно измеряется количеством запросов, а не количеством посетителей.

Теперь после введения давайте посмотрим, как можно справиться большим количеством запросов, и при этом иметь высокую скорость сайта.

Оптимизация кода сайта

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

Как? Применяя в своем проекте две простые идеи:

  1. Стремиться улучшить скорость обработки процессором каждого отдельного запроса и/или снизить потребление им памяти
  2. Сократить количество запросов, достигающих вашего сервера, с помощью различных методов кэширования

Оптимизация использования процессора и памяти для запросов

  • Статические запросы. Большинство запросов довольно просты и обрабатываются быстро. Самые быстрые запросы связаны со статическим контентом — CSS и JS-файлами, изображениями, которые обычно не требуют вычислительной мощности, но всё равно создают нагрузку на сервер. Существуют не так много эффективных методов для оптимизации таких запросов, и в большинстве случаев их следует просто кэшировать. Более подробную информацию см. в следующем разделе про кэширование.
  • Динамические запросы. Далее идут запросы, требующие вычислений, но пока без SQL-запросов к базе данных. Эти запросы более медленные, и их скорость может варьироваться в зависимости от сложности вычислений и объёма кода, который нужно выполнить. На простых сайтах такие запросы также обычно обрабатываются достаточно быстро, но на некоторых сайтах может возникнуть узкое место, особенно если в результате требуется вывод большого количества DOM-элементов.
    Некоторые функции языка могут быть менее эффективными и требовать больше времени на выполнение. Например, частое использование функций вроде preg_match() может значительно замедлить генерацию страниц. Существует ряд медленных встроенных методов в языке PHP, но в вашем проекте также могут присутствовать и пользовательские методы, которые неэффективно используют системные ресурсы. Инструменты для профилирования, такие как расширение XDebug для PHP, могут показать что именно выполняется медленно Для этой цели я включил установку XDebug в PHP-контейнер, используемый WP BOX.
  • Динамические запросы с SQL. Эти запросы могут быть самыми ресурсоёмкими, но даже среди них некоторые выполняются быстро. Например, выборка текущей страницы из базы данных, списка последних страниц или страниц по категориям в WordPress — такие стандартные запросы тоже могут быть достаточно быстры, но при реализации собственных фильтров и способов получения информации из базы данных вы можете написать и неэффективные запросы которые будут замедлять страницу (или установить такие запросы вместе с плагином или темой). Вы можете проверить производительность SQL запросов, установив плагин Query Monitor для отладки, даже без специальных знаний.

Посмотрите на скриншот ниже: это SQL-запрос для контента главной страницы https://wp-box.org. В первом столбце вы можете увидеть точный SQL-запрос, а в последнем столбце — время его обработки.

Query Monitor fast query execution
Профайлинг SQL через Query Monitor

С другой стороны, некоторые запросы могут быть медленные и по своей природе . Вам следует избегать их использования и искать альтернативные решения. Однако требования бизнес-логики иногда не оставляют выбора, и вам приходится использовать медленные запросы.

Примеры, которых стоит избегать:

  • Запрос случайных записей. Хотя на первый взгляд такой запрос может показаться быстрым, с ростом базы данных такие запросы становятся всё медленнее и серьезно снижают производительность.
  • Запрос записей по метаданным. Структура базы данных WordPress устроена так, что такие запросы выполняются медленно. И с ростом базы данных они становятся ещё медленнее. Потому что метаданные не индексируются.
  • Запросы нестандартными методами. Используйте WP_Query и $wpdb для создания пользовательских запросов, так как они изначально лучше работают с кэшированием и кэшируются и сбрасывают кэш при обновлении автоматически, если оно у вас есть.
  • Запросы по неиндексированным столбцам. Только индексированные столбцы обрабатываются достаточно быстро, поэтому кроме запросов по метаданным в целом и другие запросы по таким столбцам будут неэффективны.

Где бывают медленные запросы:

  • Поиск. Медленный, потому что ищет по частичным строкам внутри неиндексируемых столбцов.
  • Фильтры продуктов и записей. Если построены на метаполях вместо таксономий, то опять же будет медленнее.
  • Неограниченные пагинацией запросы. Как на странице магазина, так и при генерации пользовательских XML-фидов (карт сайта и других выгрузок) с данными.
  • Случайные записи или предложения товаров после основного продукта. Лучше показывать записи из связанных категорий или таксономий, предыдущие/следующие записи, либо вручную выбранные по идентификаторам.
  • Другие сложные пользовательские запросы, которые содержат множество джоинов и другую логику, по нестандартным таблицам и частично без индексов.

Для таких случаев не существует универсальных советов. Лучше всего выявить конкретные узкие места и разработать или найти готовое индивидуальное решение, если по какой-то причине вы не можете избежать использования этой логики.

Кроме того, стоит рассмотреть возможность кэширования и ленивой загрузки некоторых запросов — например, нет необходимости выполнять поисковый запрос при загрузке страницы, можно сначала получить ввод из поисковой формы, показать красивый индикатор загрузки и затем спокойно загрузить результаты. Однако это последнее замечание не относится к техникам оптимизации под высокую нагрузку, а скорее к рекомендациям по UX и скорости загрузки страницы.

Сокращение количества запросов с помощью CDN и кэширования

Это очень важная оптимизация для снижения нагрузки на ваш сервер, которая решает проблему для большинства вебсайтов. Однако следует учитывать, что она применяется только к GET запросам — загрузкам страниц, шаблонов, результатов постраничной навигации и другого контента, где не обрабатываются пользовательские данные и не отображается контент, зависящий от пользователя.

Лучшей практикой будет возвращать контент как можно раньше, не передавая запрос на следующий элемент системы.
А элементы системы могут быть следующие:

  • Кэш браузера. Вы можете сохранить некоторые статические ресурсы в кэше браузера, такие как изображения, стили и скрипты фронтенда. Убедитесь, что каждый ресурс сохраняется с уникальным параметром версии, например, styles.css?v=1. Применение версий в конце имени файла — это возможность сбросить кэш при необходимости. Если вы измените внешний вид сайт, то указав новую версию файла стилией в ресурсах страницы, вы обновите его для пользователей, у которых он был закэширован в браузере. Кроме того, ресурсы, закэшированные в браузере, загружаются у пользователя мгновенно (ведь для них не нужен интернет)!
  • CDN. Не только являются уровнем кэширования, но и физически находятся ближе к пользователю, что обеспечивает более быструю загрузку контента. Поэтому, если по каким-то причинам вы не хотите хранить некоторый статический контент в кэше браузера, вы можете хранить его в CDN с разумным временем кэширования. Обычно я настраиваю два разных правила: одно для статического контента и другое для запросов к определённым адресам, которые, могут кэшироваться от 1 минуты до 1 дня. Все остальные запросы идут уже на сервер. Также CDN может использовать и как балансировщик нагрузки на ваши сервера.
  • Nginx. Я использую его в качестве веб-сервера, он также может быть балансировщиком нагрузки. Nginx обрабатывает все входящие запросы, и обычно настраивается так, чтобы быстро возвращать статический контент и передавать запросы к PHP-скриптам в контейнер PHP или локальный PHP-сервис. Обратите внимание, что каждый раз, когда кэш истекает на вышеупомянутых уровнях, запрос будет направлен на ваш сервер для повторной загрузки контента, поэтому даже с настроенным кэшем — какая-то нагрузка будет всегда.
  • PHP и Redis. На этом уровне вы можете кэшировать как результаты запросов, так и готовые рендеры шаблонов. В процессе установки WP BOX создается контейнер для Redis и PHP, а также устанавливается WordPress плагин, который делает его доступным для использования внутри WordPress без каких-либо модификаций, так как WordPress по умолчанию кэширует запросы, если доступна служба кэширования. В некоторых случаях вы можете поместить целиком шаблоны шапки и футера сайта в кэш для более быстрой отрисовки на редко посещаемых страницах. Для сохранения кэша в редис есть бесплатный плагин Redis Object Cache.
  • База данных. В любой базе данных важно писать эффективные SQL-запросы. Я уже объяснил некоторые принципы выше. Здесь я просто хочу повторить: вам необходимо использовать индексы, чтобы сделать ваши запросы быстрыми и выдерживать высокую нагрузку на сайт.

Что нельзя кэшировать?

  • Вы не можете кэшировать всю страницу, если некоторые её части при загрузке используют объект текущего пользователя. Например, если вы показываете изображение профиля пользователя где нибудь в шапке страницы. Такой динамический контент, зависящий от пользователя, придется загружать из REST API или с помощью Ajax-запроса, а не во время первоначальной загрузки страницы, если вы хотите закэшировать страницу.
  • Другой пример — кэширование частей шаблонов: вы можете захотеть кэшировать всю шапку сайта с большим мегаменю внутри, чтобы он загружался из кэша при каждой загрузке страницы. Однако переключатель языков расположенный в шапке в этом случае также должен загружаться отдельно, ведь он на каждой странице разный.
  • Если у вас есть контент, созданный пользователями, например комментарии, лайки, различные счетчики действий и другие интерактивные элементы, вы также не можете кэшировать страницу, пока не вынесете подгрузку этих элементов с помощью скриптов на стороне фронтенда.
  • Капчи. Некоторые простые капчи генерируются на стороне сервера и несовместимы с кэшем. Используйте ReCaptcha или любую другую современную фронтенд-капчу.
  • Если вы используете wp_is_mobile(), устанавливаете куки или выполняете любые обновления базы данных во время загрузки страницы, вы также не можете кэшировать страницу. Разработайте другое решение.
  • Nonce — тоже нельзя кэшировать. При использовании — nonce появляется в теле страницы, и соответственно если вы закэшируете страницу — в какой-то момент ваш фротенд скрипт начнет использовать устаревший закэшированный nonce.

Репликация баз данных и балансировка нагрузки

Репликация баз данных — это более продвинутая техника, которая может не понадобиться большинству проектов, однако она становится полезной в трех ситуациях:

  1. У вас есть запросы к основному серверу, которые вы хотите обрабатывать быстрее
  2. У вас в целом растущее количество запросов к серверу, которые всё ещё поступают на ваш сервер, даже с кэшированием
  3. Вы реализуете blue green релизы, при которых прод подменяется на стейджинг с нулевым временем недоступности

Первый случай более известен в приложениях, таких как видеоигры, где каждая миллисекунда имеет значение. Если у вас сервер в США, а запрос поступает от пользователя из Азии, каждый запрос будет иметь задержку из-за расстояния, даже на хорошо оптимизированном сайте. CDN частично решает вопрос для GET запросов, которые могут быть закэшированы. Но если если вы хотите, чтобы ваши некэшируемые запросы также обрабатывались действительно быстро, вам потребуется чтобы и сервера производящие вычисления и запросы к БД были близки к местоположению пользователя. Это также важно и для сайтов электронной коммерции и финтеха проектов.

По аналогии такое решение потребуется и для второго случая, не ради скорости загрузки, а для того, чтобы справится с разным количеством траффика в разных регионах.

Все эти ситуации могут быть реализованы с балансировкой нагрузки и без неё.

WP BOX и Высокая Нагрузка

Автор этого проекта сталкивался с проблемами, о которых шла речь в этой статьи и разрабатывает WP BOX с учетом потенциальной высокой нагрузки.

WP BOX может не быть самым эффективным решением на рынке, потому что я пытаюсь поддерживать баланс между эффективностью и удобством использования при разумных затратах на поддержку, но он спроектирован как универсальное решение. Код, написанный для проекта, оптимизирован для разумной эффективности, не жертвуя комфортом работы с сайтом на ежедневной основе.

Этот веб-сайт развернут на Google Compute Engine и использует CDN Cloudflare, но он будет работать аналогично на любом другом VPS или вашем локальном компьютере.