Магазин на WooCommerce с несколькими сотнями товаров, меньше чем двадцатью заказами в день и без всплесков живого трафика спокойно работает на 2 ГБ памяти и полутора ядрах CPU. Магазину с несколькими сотнями заказов в день нужны 8 ГБ и три-четыре ядра. Эти два числа обрамляют большинство магазинов, которые читатель этой статьи собирается строить, и разница между ними вовсе не в трафике: она в том, какую долю сайта можно отдать из кэша, а магазин - как раз тот сайт, где ответ звучит как «меньше, чем вы думаете».
Именно это стоит понять, прежде чем что-либо покупать. Сайт-визитка или блог может отдавать каждую страницу из статического кэша, и тариф на 1 ГБ выдержит на удивление большую нагрузку. Магазин не может кэшировать корзину, оформление заказа, страницы аккаунта и остатки на складе, поэтому каждый такой запрос исполняет PHP, обращается к базе и занимает рабочий процесс на всё время, пока идёт. Подбирать ресурсы магазину - значит подбирать их под некэшируемую часть.
Что нужно WooCommerce, прежде чем считать RAM#
Программные требования невелики, и большую часть WooCommerce проверяет сам. Откройте в админке WooCommerce, затем Status: отчёт System Status оценивает ваш сервер по ожиданиям текущего релиза и красным отмечает всё, что ему не нравится. Эта страница актуальнее любой статьи, включая эту, так что таблицу ниже считайте формой ответа, а саму страницу - авторитетом.
| Требование | Работает | Комфортно |
|---|---|---|
| PHP | 7.4 | 8.2 или 8.3 |
| База данных | MySQL 5.7, MariaDB 10.5 | MySQL 8, MariaDB 10.11 |
| Лимит памяти WordPress | 256 MB | 512 MB |
| HTTPS | Обязателен | Обязателен |
| PHP max input vars | 1000 | 5000 |
HTTPS для магазина не опция. Платёжные шлюзы отказываются загружать свои скрипты по обычному HTTP, браузеры не подставляют данные карты на незащищённой странице, а оформление заказа тихо ломается так, что похоже на ошибку плагина. Сначала добейтесь работающего сертификата: выпуск разобран в статье HTTPS и Let's Encrypt простыми словами, а в статье www или apex: домен и редиректы - как убедиться, что покрыты оба имени, потому что магазин, который выдаёт ошибку на www, теряет покупателей, набравших именно его.
Из расширений PHP на деле важны mysqli, curl, mbstring, dom, xml, json, zip, fileinfo, openssl, intl, bcmath и одно из gd или imagick для изменения размера картинок. Некоторым платёжным плагинам и плагинам доставки нужен soap. Без curl ломается каждый шлюз; без gd или imagick картинки товаров никогда не уменьшаются, и медиатека растёт в десять раз быстрее, чем должна.
Сами настройки PHP - то, в чём люди ошибаются чаще всего, и их стоит задавать явно, а не наследовать:
memory_limit = 512Mmax_execution_time = 300max_input_vars = 5000upload_max_filesize = 64Mpost_max_size = 64Mopcache.enable = 1opcache.memory_consumption = 256opcache.interned_strings_buffer = 16opcache.max_accelerated_files = 20000opcache.revalidate_freq = 2Странная из них - max_input_vars. Экраны настроек WooCommerce и вариативные товары отправляют огромные формы, а когда лимит достигнут, PHP молча обрезает ввод: вы сохраняете товар с 90 вариациями, и 40 из них исчезают без единой ошибки. 5000 - это число, которое просит сам WooCommerce.
opcache.max_accelerated_files по умолчанию равен 10000, а у установки WooCommerce с двадцатью плагинами PHP-файлов больше. Когда кэш заполнен, лишнее перекомпилируется при каждом запросе, и время до первого байта растёт без видимой причины. Остальное разобрано в статье настройки PHP, которые важны.
У WordPress есть и собственный лимит поверх PHP, и по умолчанию он ниже:
define( 'WP_MEMORY_LIMIT', '256M' );define( 'WP_MAX_MEMORY_LIMIT', '512M' );Подбор ресурсов: RAM, CPU и диск по числу заказов#
Число заказов в день - лучшая входная величина, чем просмотры страниц: дорогой запрос - это заказ, а большинство просмотров отдаётся из кэша.
| Магазин | Заказов / день | RAM | vCPU | Диск |
|---|---|---|---|---|
| Запуск, небольшой каталог | до 20 | 2 GB | 1 - 1.5 | 10 GB |
| Растущий, регулярный трафик | 20 - 100 | 4 GB | 2 | 25 GB |
| Загруженный, кампании и распродажи | 100 - 500 | 8 GB | 3 - 4 | 50 GB |
| Больше этого | 500+ | Отдельная машина | 6+ | 100 GB+ |
Три фактора сдвигают магазин на строку вверх независимо от числа заказов. Размер каталога: 20 000 товаров с вариациями всерьёз нагружают базу и каждую страницу админки даже при малом числе заказов. Число плагинов: каждый активный плагин - код, загружаемый в каждом запросе; сорок плагинов - совсем другое приложение, чем десять. Профиль трафика: магазин, который набирает заказы целого месяца за два часа распродажи, должен пережить пик, а не среднее.
Диск - это в основном картинки. Закладывайте примерно 2 - 5 МБ на товар после того, как WordPress создаст все размеры миниатюр, плюс база, плюс резервные копии, если вы храните их локально, чего делать не стоит.
В RE:NODE линейка CMS в разделе веб-хостинга идёт от 1 ГБ до 8 ГБ и до четырёх vCPU, что покрывает первые три строки этой таблицы. CPU жёстко ограничен купленной долей, а не работает как разовый запас, поэтому магазин, который в распродажу сидит на 100%, работает медленно, но не приостанавливается. Выше верхней строки магазину нужна собственная машина: что меняется, когда вы берёте на себя операционную систему, описано в статье VDS или игровая панель: как выбрать.
Рабочие процессы PHP - настоящий предел параллелизма#
Память съедает не «трафик». Её съедают рабочие процессы PHP, каждый из которых обрабатывает один запрос за раз. Число рабочих процессов и решает, сколько некэшируемых запросов вы обслужите одновременно, и задаётся оно в пуле PHP-FPM:
pm = dynamicpm.max_children = 12pm.start_servers = 4pm.min_spare_servers = 2pm.max_spare_servers = 6pm.max_requests = 500Запрос WooCommerce обычно держит 64 - 128 МБ памяти PHP, так что арифметика такая: память, доступная PHP, делённая на средний размер процесса, - ваш потолок для pm.max_children. На тарифе 4 ГБ, где для PHP свободно около 3 ГБ, а процесс занимает 96 МБ, это порядка 30 - и поставить стоит меньше, потому что память нужна ещё базе и веб-серверу.
Поставить слишком много хуже, чем слишком мало. Слишком мало - запросы стоят в очереди, а это медленно. Слишком много - у ядра заканчивается память, и в RE:NODE это означает, что контейнер останавливается и перезапускается начисто, а не уходит в своп: быстрый отказ вместо медленного, но всё равно отказ. Подбирайте число процессов под ту память, что у вас есть.
pm.max_requests = 500 пересоздаёт каждый рабочий процесс после 500 запросов, что прикрывает медленные утечки памяти в плагинах. Это не исправление, но дешёвая страховка.
И ещё одно: запрос оформления заказа удерживает свой процесс на всё время обмена с платёжным шлюзом. Если шлюз отвечает три секунды, процесс пропадает на три секунды. Двенадцать процессов и медленный шлюз - это очередь, и поэтому магазин при тестировании чувствует себя хорошо, а на первой же кампании ложится.
Почему магазин нельзя целиком кэшировать постранично#
Кэширование страниц целиком - то, что делает WordPress быстрым, и его приходится отключать ровно для тех страниц, которые приносят деньги. Кэш нужно обходить всегда для:
/cart/,/checkout/и/my-account/, а также любой страницы, содержащей эти блоки или шорткоды под другим адресом.- Любого запроса с cookie
woocommerce_items_in_cartилиwoocommerce_cart_hash, либо с cookie, имя которой начинается сwp_woocommerce_session_. Посетителю с чем-то в корзине ни в коем случае нельзя отдавать закэшированную страницу другого посетителя. - Любого запроса с
add-to-cart,wc-ajaxилиremoved_itemв строке запроса.
Каждый серьёзный плагин кэширования знает эти правила и применяет их по умолчанию. Опасность - в правиле, написанном вручную на уровне прокси, или в CDN, настроенном «кэшировать всё»: именно так магазин и показывает одному покупателю корзину другого. Если вы возьмёте из этого раздела одно, возьмите список cookie.
С этим связана и ловушка производительности - фрагменты корзины. По умолчанию WooCommerce делает некэшируемый AJAX-запрос к ?wc-ajax=get_refreshed_fragments при каждой загрузке страницы, чтобы виджет корзины показывал верное число, а значит, каждый просмотр, кэшированный или нет, всё равно один раз запускает PHP. Если в шапке темы показан счётчик корзины, это крупнейший источник нагрузки. Обычно выход - загружать фрагменты только на страницах, где корзина действительно показана, либо заменить живой счётчик тем, что рисует JavaScript по cookie. Остальной стек разобран в статье скорость WordPress и кэширование.
База данных и таблицы, которые растут#
WooCommerce - это приложение для базы данных в обличье сайта. Возраст ему определяют четыре вещи.
Автозагружаемые опции. Каждый запрос до всего остального загружает в память PHP каждую строку wp_options, помеченную на автозагрузку. Плагины сваливают туда настройки, куски лицензий и кэшированные ответы API и никогда не чистят. Магазин с 3 МБ автозагружаемых опций платит эту цену на каждом запросе, включая AJAX.
SELECT option_name, LENGTH(option_value) AS bytesFROM wp_optionsWHERE autoload IN ('yes', 'on', 'auto', 'auto-on')ORDER BY bytes DESCLIMIT 20;Всё, что в этом списке больше примерно 100 КБ, требует объяснений. Просроченные transient - строки, имена которых начинаются с _transient_, - удалять безопасно, и WP-CLI делает это одной командой wp transient delete --expired.
Хранение заказов. Раньше WooCommerce хранил заказы как записи в wp_posts, а их поля - в wp_postmeta, из-за чего простой поиск заказа превращался в груду самосоединений. High-Performance Order Storage заменил это выделенными таблицами - wp_wc_orders, wp_wc_orders_meta, wp_wc_order_addresses и wp_wc_order_operational_data. Для новых установок он включён по умолчанию начиная с WooCommerce 8.2; старые магазины включают его в WooCommerce, Settings, Advanced, Features, и сначала запускается синхронизация. Если ваш магазин старше и экраны заказов в админке еле ползут, это лекарство, и выигрыш от него больше, чем от любого количества дополнительной RAM.
Action Scheduler. WooCommerce ставит фоновую работу - письма, синхронизацию остатков, продление подписок, импорт аналитики - в очередь в wp_actionscheduler_actions. Завершённые действия хранятся около месяца, а потом их удаляет задача очистки. Сама эта очистка - тоже запланированное действие, так что в магазине, где cron работает неправильно, таблица растёт без предела, и магазины с миллионами строк в ней - обычное дело. Прежде чем считать базу здоровой, проверьте число записей в WooCommerce, Status, Scheduled Actions.
Таблицы поиска. wp_wc_product_meta_lookup, wp_wc_customer_lookup и таблицы аналитики существуют для того, чтобы фильтрации и отчётам не приходилось читать wp_postmeta. Если они расходятся с действительностью, их пересобирают на экране Status, Tools - полезно знать, когда отчёты показывают числа, не совпадающие со списком заказов.
Практическая заметка о движке: WordPress и WooCommerce написаны под MySQL-совместимые базы и ничего другого использовать не могут. В RE:NODE в тарифы для сайтов входят два слота баз данных, создаваемых в панели, со сгенерированными хостом, пользователем и паролем и кнопкой Open in phpMyAdmin, которая входит по токену, действующему 60 секунд. Отдельно продаваемая линейка хостинга баз данных - это PostgreSQL и MongoDB, что для многих задач правильный инструмент, но не то, к чему подключается магазин на WordPress.
Cron: wp-cron - это не cron#
В WordPress нет планировщика. Есть проверка, которая запускается при загрузке страниц, и если что-то пора выполнить, оно выполняется в запросе этого посетителя. В магазине с ровным трафиком это в основном работает. В магазине с тихими ночами это значит, что запланированные письма, синхронизация остатков, продление подписок и очистка Action Scheduler - всё ждёт, пока кто-нибудь зайдёт; а в магазине с большим трафиком это значит, что каждый сотый посетитель платит за ваши фоновые задачи.
Замените его настоящей записью cron:
define( 'DISABLE_WP_CRON', true );# Every five minutes, from the system crontab*/5 * * * * curl -sS https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1# Better, if WP-CLI is available: no HTTP round trip, real exit codes*/5 * * * * cd /var/www/html && wp cron event run --due-now --quietПять минут - подходящий интервал для большинства магазинов. Одна минута оправданна, если у вас подписки или живая синхронизация остатков; всё, что короче, - просто нагрузка. Если синтаксис вам незнаком, в статье выражения cron простыми словами разобраны пять полей, а в статье задачи по расписанию, которые стоит завести перечислены те, что окупаются.
Сессии, объектный кэш и фрагменты корзины#
Каждый покупатель с корзиной получает строку в wp_woocommerce_sessions, ключом которой служит cookie, а чистит её ежедневное запланированное действие. В магазине, где cron сломан, эта таблица тоже растёт без предела, а поскольку её читают и пишут почти в каждом некэшируемом запросе, раздутая таблица сессий ощущается везде.
Постоянный объектный кэш - самое большое улучшение, доступное загруженному магазину. Без него WordPress перестраивает свои внутренние кэши при каждом запросе, а transient читаются из базы и пишутся в неё. С ним опции, transient и результаты запросов живут в памяти между запросами, и нагрузка на базу резко падает - нередко вдвое в магазине с большим каталогом. Для этого нужны служба Redis или Memcached и соответствующий drop-in object-cache.php.
Будьте честны в том, что для этого требуется: это отдельная служба, а не настройка. RE:NODE не продаёт управляемые Redis или Memcached, так что на общем тарифе для сайтов drop-in просто не на что направить. На VDS вы ставите и запускаете любой из них рядом с сайтом. Честная версия того, когда лишняя движущаяся часть себя оправдывает, - в статье Redis: когда он нужен, и для магазина меньше чем с двадцатью заказами в день обычно не оправдывает.
Медиафайлы, резервные копии и данные, которые не воссоздать#
Файлы на диске заменимы. Картинки товаров можно загрузить заново, плагины поставить снова, тему взять из Git. Заказы и покупателей воссоздать не из чего, поэтому именно база данных - то, над чем стоит трястись.
- Делайте копию перед каждым обновлением. Обновление плагинов в магазине - это изменение на боевой системе. Каждый раз сначала делайте копию, которую можно восстановить за пять минут.
- Храните копии вне машины. Копия на том же диске защищает от ваших ошибок, но не от отказа диска. В RE:NODE слоты резервных копий входят в любой тариф, хранятся вне машины, которую защищают, восстанавливаются кнопкой и блокируются, чтобы ротация не съела нужную. Удаление сервера удаляет его копии, включая заблокированные.
- Иногда восстанавливайте одну. Статьи резервные копии баз данных и восстановление и проверьте восстановление до того, как оно понадобится существуют потому, что копия, которую никто не восстанавливал, - это гипотеза.
- Не храните данные карт. Используйте шлюз, который вообще не пускает данные карт на ваш сервер, - через размещённое поле или редирект. Это снимает с вашего хостинга почти всю нагрузку PCI и является правильным выбором для любого магазина ниже крупного масштаба.
Измерять, а не гадать#
Прежде чем менять тариф, выясните, какой именно ресурс упирается в предел. Графики в панели показывают память, CPU и диск относительно лимитов; что означают их формы, объясняет статья как читать график нагрузки сервера. Внутри WordPress почти на все вопросы отвечают три инструмента:
- Query Monitor показывает каждый запрос на странице, сколько он длился и какой плагин его вызвал. Поставьте его на staging, воспроизведите медленную страницу, и обычно ответ виден с одного взгляда.
- WooCommerce, Status сообщает версии PHP и базы, лимит памяти, расширения и то, здоровы ли cron и запланированные действия.
- Журнал медленных запросов, если у вас есть доступ к конфигурации сервера базы данных, покажет, какой запрос действительно вам обходится, а не тот, на который вы грешите.
Магазин на правильном тарифе должен отдавать кэшированную страницу категории за серверное время много меньше 200 мс, а некэшированную страницу товара - меньше чем за 500 мс. Если медленны кэшированные страницы, проблема в сервере. Если медленны только некэшированные, проблема в PHP, плагинах или базе, и больше RAM её не коснётся.
FAQ#
Сколько RAM нужно магазину на WooCommerce?
2 ГБ для небольшого магазина до двадцати заказов в день, 4 ГБ, когда трафик стал ровным, и 8 ГБ для нескольких сотен заказов в день или каталога в десятки тысяч товаров. Память расходуют рабочие процессы PHP, так что честный вопрос - сколько одновременных некэшируемых запросов вам нужно обслужить. Общая версия ответа есть в статье сколько RAM нужно WordPress.
Можно ли использовать для WooCommerce обычный тариф WordPress?
Да, если у него есть память и настройки PHP, описанные выше. Разница не в программе, а в том, что магазин не может отдавать самые важные страницы из кэша, поэтому тот же тариф поддерживает намного меньше одновременных покупателей, чем читателей. Закладывайте примерно вдвое больше, чем понадобилось бы сайту с контентом при таком же трафике.
Нужен ли WooCommerce отдельный сервер базы данных?
Пока он не большой - нет. Две-три сотни заказов в день прекрасно работают с базой на той же машине. Отдельный сервер базы помогает, когда серверов приложения несколько или когда рабочий набор данных базы больше не помещается в память, - и он добавляет сетевой обмен к каждому запросу, так что бесплатным улучшением его не назвать.
Почему магазин тормозит только в админке?
Почти всегда дело в списке заказов или товаров, и почти всегда - в старой установке, которая всё ещё хранит заказы как записи. Включите High-Performance Order Storage, проверьте размер таблицы Action Scheduler и посмотрите на автозагружаемые опции. Это три проблемы базы данных, до которых не дотянется никакое кэширование фронтенда.
Нужен ли Redis для WooCommerce?
Только когда узким местом становится база, а для большинства магазинов это наступает позже, чем советуют. Постоянный объектный кэш - большой выигрыш для загруженного магазина с большим каталогом и мало что даёт маленькому. Ему ещё нужна существующая служба Redis или Memcached, а общий тариф для сайтов у нас её не предоставляет.
Исправит ли хостинг мои Core Web Vitals?
Отчасти. Хостингу принадлежит время до первого байта, а медленный TTFB ограничивает всё, что идёт после него. Largest Contentful Paint и сдвиг макета - это в основном тема, размеры картинок и сторонние скрипты, и переход на тариф побольше не меняет ни одного из них.




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