RE:NODE

Ресурсы14 мин чтения

Сколько RAM на самом деле нужно WordPress

Рассчитывайте WordPress по воркерам PHP, а не на глаз. Куда уходит память, чем на деле управляет memory_limit и как кэширование меняет все цифры.

0 прочтений

Небольшому сайту на WordPress (блог, сайт-визитка, портфолио, двадцать плагинов, несколько тысяч посещений в день) комфортно в 1 GB. Загруженному сайту с конструктором страниц, системой подписок или парой сотен товаров нужно 2-4 GB. Магазину на WooCommerce, который делает реальные деньги на некэшируемом трафике корзины и оформления заказа, нужно 4-8 GB, и он их использует. Сами по себе эти числа бесполезны: WordPress потребляет память не одним куском, а как некоторое число процессов PHP, умноженное на размер каждого, и обе половины этого произведения находятся под вашим контролем. Освоив эту арифметику, вы рассчитаете любой сайт на WordPress минут за пять и заранее поймёте, поможет ли план побольше или вы собираетесь платить за проблему плагина.

Короткий ответ по типам сайтов#

Тип сайтаRAMВоркеры PHPЧто ограничивает
Блог или сайт-визитка, с кэшем, лёгкая тема1 GB4-6Почти ничего
Сайт компании, конструктор страниц, 25-40 плагинов2 GB6-10Размер воркера
Подписки, LMS, форум, вошедшие в систему пользователи4 GB10-16Некэшируемые запросы
WooCommerce, до 1 000 товаров4 GB10-16Корзина и оформление заказа
WooCommerce, большой каталог или большой трафик8 GB16-30База данных, затем воркеры
Сеть сайтов (multisite)4 GB и большеНа каждый сайтСамый загруженный из них

Читайте это как отправные точки, в том же духе, что и цифры в статье требования к серверам по играм: величина, с которой начинают и которую уточняют по реальным данным, а не порог. Остальная часть статьи - о том, как это уточнение делается.

Куда на самом деле уходит память#

На плане, где весь стек работает в одном выделении, память нужна пяти вещам, и лишь одна из них - сам WordPress:

  • Веб-сервер. nginx невелик, несколько десятков мегабайт. Apache с mpm_prefork и mod_php - нет, потому что каждый процесс Apache несёт в себе интерпретатор PHP.
  • Воркеры PHP-FPM. Главная статья. Каждый воркер - это полноценный процесс PHP, который на каждый запрос загружает ядро WordPress, вашу тему и каждый активный плагин. Здесь уходит большая часть памяти и здесь находится вся полезная настройка.
  • OPcache. Единый общий блок памяти со скомпилированным байткодом PHP, размер которого задаётся один раз и которым пользуются все воркеры. По умолчанию 128 MB.
  • Сервер базы данных. Отдельный процесс с собственным кэшем страниц таблиц и индексов. На небольшом сайте несколько сотен мегабайт; на большом магазине - столько, сколько сможете дать.
  • Операционная система и всё остальное, что вы запускаете. 150-350 MB для минимальной установки Linux, ещё до того, как вы что-то добавили.

Важный структурный вывод: воркеры PHP - единственная часть, которая растёт вместе с трафиком. Всё остальное примерно постоянно. Поэтому расчёт WordPress в основном сводится к вопросу, сколько воркеров вы можете себе позволить и насколько велик каждый.

Воркеры PHP - настоящая единица измерения#

Пул PHP-FPM запускает фиксированное число рабочих процессов. Каждый обрабатывает один запрос за раз от начала до конца. Если пока все воркеры заняты приходят десять запросов, они встают в очередь; если очередь заполнена, посетители получают 502 или 504. Именно это почти всегда и означает «сайт упал под нагрузкой»: не нехватка памяти и не база данных, а просто больше одновременных запросов, чем воркеров.

Арифметика того, сколько воркеров нужно, коротка:

code
workers needed = requests per second x average request time in seconds12 req/s x 0.30 s = 3.6  ->  round up, plus headroom  ->  6 workers12 req/s x 1.20 s = 14.4 ->  round up, plus headroom  ->  18 workers

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

Арифметика размера воркера идёт с другой стороны:

code
max_children = (RAM available for PHP) / (average worker RSS)2 GB plan:  2048 - 250 (OS + nginx) - 400 (database) - 128 (OPcache) = 1270 MB1270 MB / 110 MB per worker  =  11 workers

Типичные размеры воркеров, измеренные как резидентная память прогретого процесса:

  • Чистый WordPress, лёгкая тема, десять плагинов: 40-70 MB
  • Типичный сайт компании, конструктор страниц, тридцать плагинов: 90-150 MB
  • WooCommerce или тяжёлая универсальная тема: 150-250 MB
  • Один плохо ведущий себя плагин, импортирующий большой файл: сколько позволит memory_limit

Задайте результат в конфиге пула, а не оставляйте значение по умолчанию, которое для небольшого плана обычно слишком велико:

/etc/php/8.3/fpm/pool.d/www.conf
pm = ondemandpm.max_children = 11pm.process_idle_timeout = 20spm.max_requests = 500request_terminate_timeout = 60srequest_slowlog_timeout = 5sslowlog = /var/log/php-fpm-slow.log

pm = ondemand запускает воркеры только по мере надобности и даёт им завершаться при простое, что подходит небольшому сайту с всплесками трафика и удерживает низким пол потребления в простое. pm = dynamic держит прогретый пул и лучше для сайта со стабильным трафиком. pm.max_requests перезапускает воркер после определённого числа запросов, что маскирует медленный рост памяти в плохо написанных плагинах.

php.ini: memory_limit, OPcache и настройки, которые важны#

memory_limit - это не размер вашего плана и не резервирование. Это потолок того, сколько может выделить один процесс PHP, прежде чем PHP завершит запрос фатальной ошибкой. Значение 512M не потребляет 512 MB; оно означает, что одному убежавшему запросу разрешено занять 512 MB, прежде чем его остановят.

Это различие важно из-за взаимодействия: memory_limit, умноженный на pm.max_children, - ваш худший случай. 512M на 20 воркеров - это 10 GB на плане в 4 GB. На практике все воркеры одновременно пика не достигают, но плагин, который упирается в лимит на каждом запросе в цикле, выяснит это за вас.

WordPress добавляет собственный слой, который сбивает с толку тех, кто задал memory_limit и не увидел изменений. В wp-includes/default-constants.php WordPress определяет WP_MEMORY_LIMIT как 40M для одного сайта и 64M для multisite, а WP_MAX_MEMORY_LIMIT как 256M для админки и cron. Если значение в php.ini выше, WordPress использует более высокое; если внутри админки вам нужно больше 256M, поднимите и константу WordPress:

wp-config.php
define( 'WP_MEMORY_LIMIT', '256M' );define( 'WP_MAX_MEMORY_LIMIT', '512M' );define( 'DISABLE_WP_CRON', true );

Разумные значения для обычного сайта: memory_limit = 256M в php.ini, а константы WordPress не трогайте, пока что-то не начнёт жаловаться. 128M хватает большинству сайтов и является значением PHP по умолчанию; 512M - диагностическая настройка для импорта, а не постоянная. Если для отрисовки страницы действительно нужно больше 256M, проблема в самой странице. Об остальной части файла рассказано в статье настройки PHP, которые важны.

OPcache - самая выгодная память из тех, что вы потратите, потому что она убирает шаги разбора и компиляции из каждого запроса. Два его значения по умолчанию не подходят для WordPress, и почти никто их не меняет:

php.ini
opcache.enable = 1opcache.memory_consumption = 192opcache.interned_strings_buffer = 16opcache.max_accelerated_files = 30000opcache.revalidate_freq = 2

opcache.max_accelerated_files по умолчанию равен 10000. В установке WordPress с тридцатью плагинами файлов PHP обычно больше, и когда лимит достигнут, остальные компилируются заново на каждом запросе без каких-либо предупреждений. opcache.memory_consumption по умолчанию равен 128 MB, и большой сайт способен его заполнить; когда это случается, OPcache сбрасывает всё и начинает сначала, что проявляется как периодический всплеск времени ответа, который выглядит как проблема хостинга, но ею не является. Обе настройки стоит задать даже на небольшом плане, а стоят они фиксированный блок памяти, общий для всех воркеров, а не для каждого.

Доля базы данных#

База данных WordPress обычно гораздо меньше, чем ожидают. Блог с тысячей записей занимает десятки мегабайт. Растёт она в трёх местах: ревизии записей, строки wp_options, помеченные для загрузки на каждом запросе, и (у магазинов) заказы и их метаданные.

Таблица автозагружаемых опций молча облагает налогом каждый запрос, потому что WordPress загружает её в память PHP целиком, прежде чем произойдёт что-то ещё. Проверьте её:

sql
SELECT ROUND(SUM(LENGTH(option_value)) / 1024) AS kb, COUNT(*) AS nFROM wp_optionsWHERE autoload IN ('yes', 'on', 'auto');

Измените префикс таблицы под вашу установку. В WordPress 6.6 добавили новые значения autoload, поэтому запрос проверяет три из них, а не одно. Меньше примерно 1 MB - это здорово. Несколько мегабайт означают, что плагин (обычно удалённый несколько месяцев назад) оставляет после себя данные, и каждый запрос на сайте платит за это и временем, и памятью.

Если вы запускаете собственный сервер базы данных, важнее всего настройка его кэша страниц таблиц, остальное - шум: дайте буферному пулу InnoDB достаточно места для рабочего набора ваших таблиц и следите, чтобы сервер целиком по-прежнему помещался в план. На управляемом плане вы этого не настраиваете, что одновременно и ограничение, и облегчение. Веб-тарифы RE:NODE включают слоты баз данных, которые создаются из панели со сгенерированными хостом, пользователем и паролем, и кнопку «Open in phpMyAdmin», которая входит по одноразовому токену со сроком действия шестьдесят секунд: этого достаточно, чтобы выполнить запрос выше и выгрузить дамп, но не для постоянной работы. Об ограничениях, с которыми вы столкнётесь на большой базе, рассказано в статье импорт и экспорт через phpMyAdmin.

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

Кэширование полностью меняет арифметику#

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

Слои в порядке того, насколько они влияют:

  1. Кэш страниц. Отрисованный HTML сохраняется и отдаётся без обращения к PHP и базе данных. Страница из кэша обходится в единицы миллисекунд и не занимает воркер. Это разница между четырьмя воркерами и сорока.
  2. OPcache. Скомпилированный байткод, о нём выше. Всегда включён, всегда стоит того.
  3. Кэш объектов. Постоянное хранилище результатов, которые WordPress сейчас вычисляет заново на каждом запросе. Требует, чтобы был доступен Redis или Memcached, а на большинстве общих хостингов PHP этого нет, и без него WordPress откатывается к кэшу на время запроса, который выбрасывается в конце каждой загрузки страницы. О том, когда это стоит организовывать, рассказано в статье Redis и когда он действительно нужен.
  4. Кэширование в браузере и на CDN. Заголовки, благодаря которым повторные посетители и граничные узлы вообще не обращаются к серверу. Справочник - заголовки HTTP-кэширования простыми словами.

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

Измеряем свой сайт#

Пять минут измерений лучше любой таблицы, включая ту, что выше.

Сначала размер воркера. На машине, где у вас есть shell, эта команда выводит резидентную память каждого процесса PHP, от самых больших:

bash
$ ps -eo rss,comm --sort=-rss | grep php-fpm | head -15$ ps -eo rss,comm | grep php-fpm | awk '{s+=$1; n++} END {print s/n/1024 " MB avg"}'

Резидентная память слегка завышает, потому что воркеры делят страницы родительского процесса и OPcache, так что воспринимайте среднее как безопасную верхнюю границу, а не точную величину. Затем включите страницу статуса FPM и посмотрите, чем на самом деле занят пул:

pool.d/www.conf
pm.status_path = /status

listen queue выше нуля означает, что запросы ждут воркера. max children reached выше нуля означает, что с последнего перезапуска они кончались хотя бы раз. Эти два счётчика прямо отвечают на вопрос «нужен ли мне план побольше», и ничто другое не отвечает.

Изнутри WordPress остальное показывает WP-CLI:

bash
$ wp db size --tables --human-readable$ wp plugin list --status=active --format=count$ wp option get cron --format=json | head -c 400$ wp transient delete --expired

Установите Query Monitor на staging-копии и загрузите самую медленную страницу, войдя как администратор. Он выведет пиковую память этого запроса, число запросов к базе данных, самые медленные из них и все HTTP-запросы, которые страница делала к внешним службам. Именно в последней панели прячутся настоящие сюрпризы: плагин, обращающийся к серверу лицензий при каждой загрузке страницы, держит воркер столько, сколько этот сервер отвечает, а воркер, занятый две секунды, - это воркер, который никого не обслуживает.

На хостинге с панелью аналог первой половины - графики в консоли, которые показывают память, CPU и диск на фоне лимитов плана. Шум запросов веб-сервера свёрнут за счётчик, который можно отключить, поэтому журнал остаётся читаемым, пока идёт нагрузочный тест.

Когда покупать больше, а когда чинить сайт#

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

Не покупайте память в этих случаях, потому что она не поможет:

  • Медленные страницы без трафика. Один запрос, который идёт три секунды на простаивающем сервере, - это проблема кода. Больше памяти превращает его в трёхсекундный запрос с большим объёмом памяти.
  • Средний воркер выше 200 MB на обычном сайте. Что-то загружает слишком много. Отключайте плагины половинами на staging-копии, пока среднее не упадёт.
  • Тайм-ауты ровно на 30 или 60 секундах. Это max_execution_time или request_terminate_timeout, а не память.
  • Забивание admin-ajax. Heartbeat или плагин, опрашивающий раз в несколько секунд, расходует воркеры без единого посетителя. Ограничьте его.
  • WordPress cron. По умолчанию wp-cron.php срабатывает при загрузках страниц, поэтому горячий момент заодно становится моментом плановых задач. Задайте DISABLE_WP_CRON и запускайте его из настоящего планировщика; на RE:NODE это вкладка Schedules с cron-выражением и упорядоченными задачами.

Ещё одно честное замечание: WordPress устанавливаете вы, а не мы. На RE:NODE нет установки в один клик и нет магазина готовых стеков. Вы получаете контейнер с окружением выполнения, SFTP и файловым менеджером, слоты баз данных, слот прокси, который выпускает и продлевает сертификат, как только вы направите запись A на показанный адрес, и слоты резервных копий. Пятнадцать минут, которые это занимает, описаны в статье установка WordPress вручную, а для уже существующего сайта - перенос WordPress на новый хостинг.

Если сайт стоит за этим слотом прокси, помните, что PHP видит адрес прокси как адрес клиента, пока вы не скажете иначе. Настоящий IP посетителя приходит в заголовке X-Forwarded-For, и плагины безопасности, журналирование комментариев и ограничение частоты читают неверное значение, пока WordPress не настроен ему доверять. Это самый частый сюрприз после переезда.

FAQ#

Хватит ли 1 GB для WordPress?

Для блога или сайта-визитки с включённым кэшем страниц - да, с запасом. Этого хватает на операционную систему, небольшую базу данных, OPcache и четыре-шесть воркеров PHP, а это больше параллелизма, чем большинство небольших сайтов когда-либо использует. Для WooCommerce, сайта с подписками или конструктора страниц с сорока плагинами этого недостаточно.

Какой memory_limit задать?

256M для обычного сайта. Это потолок на запрос, а не выделение, поэтому большее значение ничего не стоит, пока что-то реально его не использует, но в худшем случае оно ещё и умножается на число воркеров. Если странице действительно нужно больше 256M для отрисовки, чините страницу, а не поднимайте число.

Почему сайт падает на пике, а график памяти в порядке?

Потому что кончились воркеры PHP, а не память. Запросы копятся за занятыми воркерами и в итоге завершаются по тайм-ауту ошибками 502 или 504, пока общая память держится далеко от лимита. Проверьте max children reached на странице статуса FPM и либо поднимите pm.max_children, если память свободна, либо сделайте так, чтобы запросы завершались быстрее.

Правда ли, что WooCommerce нужно так много больше?

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

Сделает ли больше RAM мой сайт быстрее?

Только если она у вас сейчас заканчивается. Память покупает параллелизм, а не скорость. Одна загрузка страницы занимает одинаковое время и на 1 GB, и на 8 GB, если ничто не конкурирует за ресурсы. Страницу ускоряют кэширование, меньше плагинов, меньше запросов к базе и более быстрое ядро CPU; о том, что хостинг может и чего не может изменить, читайте в статье TTFB, Core Web Vitals и хостинг.

Сколько посетителей выдержит 2 GB?

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


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000