MAATRIX / Блог / Сколько запросов держит WordPress без кеша и с ним: честный замер на своём сервере

Сколько запросов держит WordPress без кеша и с ним: честный замер на своём сервере

MAATRIX

Один и тот же WordPress на одном и том же сервере может держать и десяток запросов в секунду, и несколько тысяч — разница не в железе, а в том, включён ли кеш и какой именно. Без кеша каждый визит — это полный цикл PHP плюс десятки запросов к MySQL, и потолок определяется процессором и базой. С кешем большая часть запросов вообще не доходит до PHP, и потолок сдвигается к nginx и сети. Разберём, где именно узкое место в каждом из режимов и как замерить это на своём сервере, а не поверить чужим цифрам из интернета.

Что реально происходит без кеша на каждый запрос

Когда в WordPress нет ни одного слоя кеширования (ни страничного, ни объектного), обращение анонимного посетителя к любой странице — это не отдача готового HTML, а полноценное выполнение PHP-приложения с нуля:

  1. Веб-сервер (nginx или Apache) принимает запрос и передаёт его в PHP-FPM (или mod_php).
  2. PHP-процесс поднимает всё ядро WordPress: подключает wp-load.php, инициализирует базу опций, активные плагины, тему — это десятки, а иногда сотни require/include на каждый хит.
  3. Срабатывают хуки init, wp, template_redirect — каждый активный плагин добавляет сюда свой код, и это не бесплатно: чем больше плагинов, тем длиннее цепочка.
  4. Формируется и выполняется набор SQL-запросов к MySQL: получить сам пост, метаданные, термины таксономий, виджеты, меню, настройки темы. В типовой установке с несколькими популярными плагинами (SEO, форма обратной связи, галерея) счёт запросов к БД на одну страницу легко уходит за пару десятков — конкретное число сильно зависит от вашего набора плагинов, здесь и далее это ориентир, а не измеренная константа.
  5. PHP собирает HTML через систему шаблонов темы и отдаёт готовую страницу обратно веб-серверу.

Каждый из этих шагов стоит времени CPU и, что важнее для предела RPS, времени ожидания ответа от MySQL. Если один запрос обрабатывается 150–300 мс (условно, у вас будет своё число), а PHP-FPM пул ограничен, скажем, 10–20 воркерами, простая арифметика — количество воркеров, делённое на среднее время ответа — даёт грубую оценку теоретического потолка. На практике он ещё ниже, потому что воркеры не освобождаются мгновенно и в очереди накапливается задержка. Разбор того, сколько сайтов и запросов вообще выдерживает один пул PHP-FPM до отказа с 502, — отдельная тема, детально она разобрана в статье сколько сайтов держит один пул PHP-FPM до 502.

Узкое место без кеша: MySQL и PHP-FPM, а не сеть и не nginx

Без кеша сеть и сам nginx почти никогда не становятся ограничением раньше, чем PHP и база — они просто не успевают загрузиться, потому что запросы застревают на предыдущем шаге. Узкое место распределяется между двумя ресурсами:

  • PHP-FPM пул. Директива pm.max_children жёстко ограничивает число одновременно обрабатываемых PHP-запросов. Как только все воркеры заняты, новые запросы встают в очередь на уровне сокета, и при накоплении очереди сверх listen.backlog клиенты начинают получать 502 от nginx. Проверить текущее состояние пула:
# статус PHP-FPM, если включён pm.status_path
curl http://127.0.0.1/fpm-status?full

# сколько процессов php-fpm сейчас живо
ps aux | grep '[p]hp-fpm' | wc -l
  • MySQL. Каждый PHP-воркер, ожидающий ответа от базы, держит слот в пуле занятым — так что реальный потолок PHP-FPM часто определяется не CPU, а тем, как быстро отвечает MySQL. Медленные запросы (например, без нужного индекса, что бывает после установки очередного плагина без ревью его SQL) размножают эту проблему: один медленный запрос держит и воркер PHP-FPM, и слот соединения MySQL одновременно. Посмотреть текущую нагрузку:
-- сколько соединений открыто прямо сейчас
SHOW STATUS LIKE 'Threads_connected';

-- запросы, которые выполняются дольше секунды
SHOW FULL PROCESSLIST;

Если у вас включён slow query log, стоит его проверить в первую очередь — про типичные причины и разбор медленных запросов MySQL есть отдельная статья MySQL медленные запросы: причины и решение. Важный нюанс: max_connections в MySQL и pm.max_children в PHP-FPM должны быть согласованы по порядку величины — если PHP-FPM способен открыть больше параллельных соединений к базе, чем MySQL готов принять, вы получите ошибки Too many connections раньше, чем упрётесь в CPU.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Что меняется, когда включается кеш: сдвиг узкого места к nginx и сети

Полноценный страничный (full-page) кеш — например, через fastcgi_cache в nginx, Varnish или плагин вроде WP Super Cache — устраняет из цепочки PHP и MySQL целиком для тех запросов, которые он способен обслужить. Nginx отдаёт заранее сохранённый на диске или в памяти HTML напрямую, даже не обращаясь к PHP-FPM. Это принципиально другой режим работы: сервер перестаёт быть «приложением с базой» и на время отдачи закешированных страниц превращается в статический файловый сервер.

В этом режиме узкое место перемещается туда, где раньше был огромный запас: в сам nginx и сеть. Актуальными становятся те же ограничения, что для любого высоконагруженного статического отдающего сервера — worker_connections, лимиты файловых дескрипторов ОС, пропускная способность канала, TLS-хендшейк на HTTPS. Методика замера этого предела — отдельная и довольно объёмная тема, подробно она разобрана в статье сколько соединений держит ваш nginx: замер до отказа; по сути, закешированный WordPress с точки зрения нагрузки — это тот же самый вопрос, только с WordPress-страницами вместо любых других статических файлов.

Важная оговорка: страничный кеш работает только для тех запросов, которые действительно можно кешировать — обычно это анонимные GET-запросы без cookie сессии. Страница корзины, оформление заказа, личный кабинет, админка — всё это по определению динамическое и через full-page кеш не проходит (или проходит, только если вы явно исключили эти пути из правил кеширования). Если у вас интернет-магазин на WooCommerce, доля кешируемого трафика может оказаться заметно ниже, чем на блоге — это стоит учитывать при интерпретации замеров.

Типы кеша и что именно каждый даёт

Разные слои кеша решают разные узкие места, и путать их — частая ошибка при диагностике «кеш есть, а быстрее не стало»:

Тип кешаЧто кешируетЧто убирает из цепочкиРаботает для залогиненных/динамики
OPcacheСкомпилированный байткод PHP-файловПовторную компиляцию PHP на каждый запросДа, всегда
Object cache (Redis/Memcached)Результаты отдельных запросов к БД и вычислений WordPress (транзиенты, объекты постов)Часть нагрузки на MySQL, но не сам PHPДа, ключевое преимущество
Full-page cache (fastcgi_cache/Varnish/плагин)Готовый HTML-ответ целикомPHP и MySQL полностью, для попавших в кеш запросовНет, только для кешируемых анонимных запросов
Браузерный/CDN-кешСтатику (изображения, CSS, JS), иногда HTML на грани CDNНагрузку на ваш сервер вообще — запрос до него не доходитЗависит от настройки CDN

OPcache почти всегда должен быть включён вне зависимости от остального — это дешёвый выигрыш без побочных эффектов, отключать его ради «честного замера без кеша» не нужно: это не тот кеш, о котором обычно идёт речь, когда сравнивают «WordPress с кешем и без». Под «без кеша» в контексте этой статьи понимается отсутствие страничного и объектного кеша — именно они определяют разницу на порядки.

Object cache через Redis не даёт такого взрывного роста RPS, как full-page кеш, потому что PHP всё равно выполняется на каждый запрос — но он резко снижает нагрузку на MySQL и особенно полезен там, где full-page кеш не работает: для залогиненных пользователей, WooCommerce, форумов. Установка и базовая настройка Redis под WordPress разобрана в статье как установить и настроить Redis на VPS, а нюансы конкретно для fastcgi_cache в nginx — в статье как установить и настроить кеширование nginx на VPS. Общий обзор способов ускорить WordPress на слабом VPS, включая выбор между этими типами кеша, есть в статье ускорение WordPress на слабом VPS.

Как замерить свой сервер без кеша

Прежде чем что-то оптимизировать, зафиксируйте реальную точку отсчёта на своём сервере — чужие цифры из статей (включая эту) не подставляются в ваш конкретный случай из-за разницы в железе, версии PHP, наборе плагинов и структуре базы.

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

Отключаем страничный и объектный кеш, оставляя OPcache включённым:

// wp-config.php — убедиться, что нет форсированного кеша
define( 'WP_CACHE', false );

Плюс деактивируйте кеширующий плагин и, если используете fastcgi_cache, временно уберите его из location для тестового домена.

Генерируем нагрузку с постепенным наращиванием параллелизма, а не сразу «на полную», чтобы увидеть момент деградации, а не только факт падения:

wrk -t4 -c20  -d30s --latency https://тестовый-домен/
wrk -t4 -c50  -d30s --latency https://тестовый-домен/
wrk -t4 -c100 -d30s --latency https://тестовый-домен/

Одновременно смотрим на все узкие места сразу, а не по очереди постфактум:

# занятость пула PHP-FPM
watch -n 1 'curl -s http://127.0.0.1/fpm-status'

# соединения и медленные запросы MySQL
watch -n 1 'mysqladmin status'

# CPU по ядрам
mpstat -P ALL 1

Фиксируйте на каждом шаге: рост p99-латентности из вывода wrk --latency, появление 502/504 в логе nginx, момент, когда все воркеры PHP-FPM заняты одновременно (curl fpm-status покажет active processes равным max children), и рост Threads_connected в MySQL к пределу max_connections. Первый из этих сигналов, который проявился, и указывает на реальное узкое место — часто это PHP-FPM пул, упирающийся в лимит воркеров, а за ним уже — MySQL.

Как замерить с кешем и не обмануть самого себя

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

Прогрейте кеш перед тестом. Первый запрос к каждому URL всегда идёт мимо кеша (кеш ещё пуст) и по скорости ничем не отличается от полностью некешированного режима. Если тест ударит по большому числу разных URL без предварительного прогрева, вы фактически измерите не «работу кеша», а смесь холодных и горячих попаданий, и результат окажется занижен и нестабилен.

Проверяйте, что кеш реально срабатывает, а не тихо обходится. Самая частая причина ложного «кеш не дал эффекта» — тестовый запрос содержит cookie, из-за которой правило кеширования в nginx намеренно пропускает его мимо кеша (типичная практика — не кешировать запросы с cookie авторизации или корзины WooCommerce). Убедитесь, что заголовок ответа явно показывает попадание в кеш:

curl -I https://тестовый-домен/ | grep -i cache

# для fastcgi_cache, если добавлена своя переменная в конфиг
# add_header X-Cache-Status $upstream_cache_status;

Значение вроде HIT подтверждает, что запрос действительно обслужен из кеша, а не ушёл в PHP по умолчанию.

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

wrk -t8 -c500  -d30s --latency https://тестовый-домен/
wrk -t8 -c2000 -d30s --latency https://тестовый-домен/

Следите за nginx, а не за PHP-FPM и MySQL — в этом режиме они почти простаивают, и основной интерес представляют Active connections из stub_status, память worker-процессов nginx и пропускная способность сетевого интерфейса (sar -n DEV 1 или iftop). Если тест упирается в CPU или память самого генератора нагрузки (wrk), а не тестируемого сервера — это тоже нередкая ошибка, стоит проверить ресурсы машины, с которой запускаете тест.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Насколько именно вырастет RPS с кешем — в разы или на порядки?

Зависит от доли кешируемого трафика и от того, насколько тяжёлым было выполнение PHP до этого. Для блога с преобладанием анонимных посетителей эффект обычно ближе к «на порядки», для интернет-магазина с большой долей залогиненных и динамических страниц — куда скромнее, потому что значительная часть трафика вообще не попадает в full-page кеш. Точную цифру для вашего сайта даст только замер по описанной методике.

Стоит ли включать object cache (Redis), если уже есть full-page кеш?

Да — они решают разные задачи. Full-page кеш ускоряет анонимные закешированные страницы, а object cache снижает нагрузку на MySQL для всего остального: админки, залогиненных пользователей, AJAX-запросов, WooCommerce. Комбинация обоих слоёв обычно даёт лучший результат, чем любой один из них.

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

Частая причина — кеш реально работает и ускоряет ответ, но старое узкое место (PHP-FPM, MySQL) было не единственным потребителем ресурсов, и освободившиеся мощности почти сразу забирает возросший трафик или фоновые задачи (cron, индексация, бэкапы). Стоит проверить CPU и I/O отдельно от RPS, не полагаясь только на субъективное «стало быстрее».

Нужно ли тестировать HTTPS отдельно от HTTP?

Да. TLS-хендшейк добавляет заметную нагрузку на CPU при большом числе новых (не keep-alive) соединений, и в закешированном режиме, где узким местом становится именно nginx, это может ощутимо снизить потолок по сравнению с чистым HTTP-тестом.

Можно ли доверять готовым бенчмаркам WordPress из интернета?

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

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →