• База знаний
  • /
  • Блог
  • /
  • Wiki
  • /
  • ONLINE CHAT
+380 (44) 364 05 71

Статья также доступна на украинском (перейти к просмотру).

Узнайте, как OPcache, Redis, APCu и PHP-FPM помогают ускорить сайт без изменения оборудования

Содержание:

В предыдущих статьях мы уже рассматривали вопросы скорости работы приложений на VPS в зависимости от оборудования и типа виртуализации. Также выяснили, что такое трассировка (трейсинг) и как её использование помогает выявить узкие места в программе. Теперь сосредоточимся на ускорении работы приложения за счет оптимизации самого PHP, в частности, улучшения механизмов кэширования кода и данных, а также выбора оптимального режима работы препроцессора для каждого типа веб-приложения.

Зачем нужна оптимизация работы PHP на VPS

Оптимизация PHP — один из способов повысить производительность приложения при неизменных характеристиках оборудования. Эта идея многократно подтверждалась на практике с выходом новых релизов PHP — производительность с каждой новой версией уверенно растёт.

Параметр TTFB (Time to First Byte) — один из базовых показателей производительности любого приложения или веб-страницы. Он определяет время ответа VPS-сервера на запрос клиента — чем меньше, тем лучше.

Выделим основные пути улучшения этой характеристики:

 Расширение или усовершенствование работы серверного оборудования;

  • применение систем CMS и CDN управления контентом и его доставки по сети;

  • Уменьшение размера данных;

  • Кэширование;

  • Оптимизация работы PHP.

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

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

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

OPcache — кеш байткода

Компилятор препроцессора PHP превращает исходный код приложения в промежуточный низкоуровневый байткод, который сразу же выполняется и после этого уничтожается. И так при каждом запросе. В сущности, происходит повторение компилятором одного и того же действия с созданием постоянной нагрузки на серверные ресурсы. Особенно это ощущается при выполнении одинаковых запросов с компиляцией одного и того же участка исходного кода сценария. Такая ситуация может быть оправдана только в том случае, если на production-серверах исходный код приложения будет ежесекундно меняться, чего, как правило, не бывает в реальности. Для этого естественный путь оптимизации этого процесса – это кэширование байткода в RAM-памяти.

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

OPcache — расширение, выполняющее две функции: кэширование байткода в RAM и его оптимизацию. Оба эти процесса ускоряют обработку кода. Начиная с PHP 5.5.0, OPcache входит в ядро препроцессора и включён по умолчанию, что упрощает его использование. Расширение официально рекомендуется для кэширования и поддерживается технически.

OPcache заменил известный кешер APC (Alternative PHP Cache), последний релиз которого 3.1.13 работает только с PHP 5.4, а разработка прекращена. В отличие от APC, OPcache эффективнее использует общую память PHP, обходя управление её фрагментацией, освобождением и возвратом, что экономит ресурсы и повышает эффективность кэша.

Начиная с версии PHP 8.0 появилась надстройка над OPCache – JIT-компилятор. Он компилирует исходный код программы не в байткод, а сразу в машинный код и размещает его в RAM-памяти. Это обеспечивает еще больший уровень оптимизации работы препроцессора. Управлять работой JIT-компилятора можно с помощью опций, размещенных в файле php.ini, о чем поговорим позже.

Установка и настройка

Для версий препроцессора ≥ 5.5.0 OPCache уже присутствует в ядре и включен, и поэтому его устанавливать не нужно. Соответственно, опция opcache.enable в файле php.ini будет также включена. Чтобы проверить это, можно воспользоваться следующей командой:

# php -r 'phpinfo();' | grep opcache.enable

Для версий из диапазона 5.2 ≤ 5.4 расширение можно установить с помощью следующей команды:

# pecl install zendopcache-beta

Для оптимальной работы расширения рекомендуются следующие значения параметров, указанных в файле php.ini:

opcache.memory_consumption=128
opcache.max_accelerated_files=8000
opcache.revalidate_freq=60
opcache.validate_timestamps=1
opcache.interned_strings_buffer=8
opcache.enable=1
opcache.fast_shutdown=1
opcache.jit=tracing/on

Дадим краткие объяснения по указанным директивам.

  • opcache.memory_consumption – определяет размер общей (общий) RAM-памяти для кэширования, минимальное значение равно 8;
  • opcache.max_accelerated_files – устанавливает максимальное количество ключей для расширения, что определяет количество сценариев для обработки. Минимальное значение – 200, максимальное – 1000000;
  • opcache.revalidate_freq – устанавливает значение временного интервала (в секундах) для проверки обновлений меток времени сценария; 
  • opcache.validate_timestamps – включает проверку сценариев в кэше на предмет их обновления через количество секунд, определенных параметром opcache.revalidate_freq. В случае отключения следует перезагрузить веб-сервер, чтобы изменения начали действовать;
  • opcache.interned_strings_buffer –определяет размер памяти RAM (Mb) для хранения интернированных сроков. Максимальные значения 32767 и 4095 на 64-х и 32-х разрядных архитектурах соответственно;
  • opcache.enable– включает кэш, опцию нельзя включить при выполнении кода, но можно отключить;
  • opcache.fast_shutdown – устанавливает быстрый алгоритм прекращения работы, при котором за быстрое освобождение памяти от наборов переменных отвечает стандартный менеджер памяти.
  • opcache.jit – управление режимами работы JIT-компилятора с помощью четырех срочных директив (функция, трассировка/вкл., выкл., отключить). Значение трассировка/вкл. установлено по умолчанию и включает компилятор с компиляцией трейсов. 

Есть и другие важные параметры, которые можно найти в документации PHP по использованию OPCache.

APCu – кэш данных в shared memory

Расширение APCu (APC User Cache) позволяет кэшировать в общей памяти сервера данные приложений на уровне пользователя. Это снижает потребность в применении ресурсозатратных запросов к базам данных и минимизирует количество операций с файловой системой. Все это вместе обеспечивает экономию машинных ресурсов и оптимизирует производительность приложения. Средство наиболее подходит для оптимизации высоконагруженных веб-приложений с высоким уровнем трафика, размещенных на локальных серверах. Поддерживается для всех версий препроцессора из диапазона PHP 5.5+. 

Таким образом, можно выделить следующие преимущества использования APCu:

  • Данные находятся в RAM-памяти, что гарантирует быстрый доступ и считывание;

  • Данные доступны для запросов и процессов PHP;

  • Улучшение масштабирования приложения;

  • Снижение нагрузки на базу данных;

  • Минимизация операций с файловой системой;

  • Простой API для управления записями кэша.

Установка и настройка

Установить расширение на ОС Mint/Debian/Ubuntu можно с помощью следующей команды:

# sudo apt install php-apcu 

После этого его нужно включить в файле php.ini:

расширение=apcu.so

После перезагрузки веб-сервера в файл конфигурации config.php приложения ownClowd следует добавить следующую строчку: 

'memcache.local' => '\OC\Memcache\APCu',

Эта директива активирует APCu для управления локальным кэшем сервера

Приведем возможные значения основных параметров APCu, которые должны присутствовать в файле php.ini:

apc.enabled=1
apc.ttl=1800
apc.shm_size=128M
apc.gc_ttl=800
apc.enable_cli=1 
apc.enabled – включает расширение;
apc.ttl –задает время хранения данных в секундах;
apc.shm_size –определяет размер сегмента RAM-памяти;
apc.gc_ttl –задает временной интервал для очистки данных неиспользуемых кэша;
apc.enable_cli – включает/выключает APCu для CLI. По умолчанию отключены, однако в дальнейшем возможны проблемы с выполнением задач крон,
и поэтому опцию лучше включить.

Использование

Библиотека APCu предоставляет широкие возможности для управления кэшированными данными. Основными операциями являются загрузка данных в кэш, их получение и удаление. Соответствующие функции библиотеки приведены ниже:

apcu_store();
apcu_fetch();
apcu_delete();
Пример хранения в кэш:
$newdata = "This is an example of storing data in cache.";
$key = "one_key";
$ttl = 3600; // Дані кешуются один час
apcu_store($key, $newdata, $ttl); 

Пример получения данных из кэша: 

$key = "one_key";
$data = apcu_fetch($key);
if ($data !== FALSE) {
echo "Data: " . $data;
} else {
echo "The requested data is not in the cache.";
} 

Пример очистки кэша:

function clearAllCache(): bool
{
return apcu_clear_cache();
}
clearAllCache();  // Обеспечивает полное удаление всех записей кэша
Приведем пример получения статистических данных по использованию памяти кэша с помощью функции apcu_cache_info() библиотеки APCu: 
function getCacheInfo(): array
{
return apcu_cache_info();
}
//Вывод статистических данных и коэффициентов попадания и отказов
print_r(getCacheInfo());

Redis – кэш данных + улучшенные возможности

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

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

Redis c PHP-модулем и поэтому требует установки и настройки. Рекомендуемая версия 2.2.6+.

Установка и настройка

Установить Redis на ОС Mint/Debian/Ubuntu можно с помощью следующей команды:

# sudo apt install redis-server php-redis

Інсталятор модуля обеспечит автоматический запуск редис-серверуи настроит его на запуск при каждой загрузке системы.

После перезагрузки веб-сервера модуль будет готов к использованию.

Убедитесь, что  даемон Redis запущенный в системе можно с помощью известной команды для формирования списка запущенных в Linux процессов:

 # ps ax | grep redis

Выход команды должен быть примерно следующим: 

31215 ? Ssl0:00 /usr/bin/redis-server 127.0.0.1:6379

Если все хорошо, следует перейти к следующему этапу – настройка конфигурации Redis в файле конфигурации config.php приложения ownClowd.Пример таких настроек для случая локального кэша сервера приведен ниже:

'memcache.local' => '\OC\Memcache\Redis',
'redis' => array(
 'host' => 'localhost',
 'port' => 6379,
 'timeout' => 0.0,
  ),

 Здесь подключение к кэшу Redisо существляется по протоколу TCP, что несколько замедляет работу с данными по сравнению с APCu. И потому в случае локального кэширования лучше наладить Redis на прослушивание socketu Unix. Продемонстрируем это, но прежде следует выполнить ряд подготовительных действий.

 Добавить пользователя www-data в группу Redis:

# sudo usermod -G redis -a www-data

 Создать директорию для Unix-сокетов:

# sudo mkdir -p /var/run/myredis/

Установить разрешения:

# sudo chown -R redis:www-data /var/run/myredis

Внести изменения в файл конфигурации Redis:

# создание сокета домена unix для прослушивания
unix-сокет /var/run/myredis/redis.sock
# установка разрешений для сокета
unixsocketperm 770
Сохранить внесенные изменения и перезагрузить Redis-сервер.
После этого остается внести в файл config.php следующие записи:
'filelocking.enabled' => true,
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.local' => '\OC\Memcache\Redis',
'redis' => array(
 'host' => '/var/run/myredis/redis.sock',
 'port' => 0,
 'timeout' => 0.0,
  ),

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

Совместное использование различных систем кэширования данных

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

  • Приложение распределено или локально;

  • Достаточный ли объем RAM-памяти имеет сервер;

  • Нужна ли транзакционная блокировка файлов;

  • Есть ли возможность и желание использовать несколько бэкендов кэширования.

После формирования ответов на указанные вопросы возможны следующие решения по оптимизации системы кэширования:

Для локальных систем со значительным объемом общей памяти может быть использовано средство APCu. Это будет способствовать высокой скорости работы и простоте отладки и эксплуатации. В то же время для транзакционной блокировки файлов следует применить Redis. Пример соответствующих настроек приведен ниже. 

'filelocking.enabled' => true, //Включение транзакционной блокировки файлов
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.local' => '\OC\Memcache\APCu',
'redis' => [
'host' => 'localhost',
'port' => 6379,
],

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

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

 'filelocking.enabled' => true, //Включение транзакционной блокировки файлов
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.local' => '\OC\Memcache\Redis',
'redis' => [
'host' => '/var/run/myredis/redis.sock',
'port' => 0,
],

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

'filelocking.enabled' => true,
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'memcache.local' => '\OC\Memcache\APCu',
'redis' => [
'host' => '10.30.51.73', // IP-адрес хоста
    'host' => '15.35.55.71', 
    'host' => 'mainserver', // Имя сервера 
    'port' => 6379,
],

Следует отметить, что во всех приведенных примерах применяется комбинация технологий: OPcache+APCu + Redis, поскольку OPcache, как уже отмечалось, входит в состав ядра препроцессора и не требует дополнительного включения. Это означает, что в нашем случае кэширование происходит как на уровне кода (OPcache), да и на уровне данных (APCu + Redis), что обеспечивает более высокий уровень производительности веб-приложений.

PHP-FPM – оптимальная настройка процессов

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

Режим PHP-FPM поддерживает три стратегии управления дочерними процессами:

  • Статический (static);

  • Динамический (dynamic);

  • "По требованию" (ondemand).

Каждый из них по-своему полезен, исходя из потребностей и характеристик веб-приложения. Рассмотрим каждый из них более подробно.

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

pm – включает режим

pm.max_children – указывает максимальное количество процессов

Для Debian/Ubuntu такие настройки можно сделать в файле /etc/php/7.4/fpm/pool.d/www.conf

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

Динамический режим обеспечивает регулировку количества процессов для обработки запросов, гарантируя при этом наличие одного «свободного». Для управления им задействованы следующие параметры:

  • pm.start_servers – количество процессов на старте PHP;

  • pm.max_children – максимальное количество процессов для запуска;

  • pm.process_idle_timeout – указывает время, сколько процесс может «не работать»

  • pm.min_spare_servers – минимальное количество «неработающих» процессов;

  • pm.max_spare_servers – максимальное количество «неработающих» процессов.

Это наиболее универсальный режим при условии, если правильно рассчитаны значения указанных параметров. Существуют разные методики для их расчета. Согласно одной из них значение наиболее важного параметра pm.max_children рассчитывается следующим образом:

(Общий объем RAM) – (Объем для системных нужд ОС, БД и т.д.)/(Объем для одного процесса) 

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

Режим применим для веб-ресурсов со значительным трафиком, но при условии правильного расчета параметров.

Режим Ondemand обеспечивает создание нового процесса при получении запроса. Его «главный» параметр:

pm.max_requests – определяет количество запросов, выполняемых всеми процессами перед респауном.

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

Оптимизация с помощью пулов

Оптимизировать работу приложения в режиме PHP-FPM можно также путем использования нескольких пулов. Наиболее распространенный вариант – создать отдельные пулы для фронтендов и бэкендов частей веб-ресурса. В таком случае пулы будут принадлежать одному и тому же пользователю, но иметь разные конфигурации менеджеру процессов, подключенные через разные сокеты. Например, для фронтенд-части можно включить самый «быстрый» режим статический, а для бекенду – ondemand. В результате, посетители магазина будут получать быстрый ответ сервера, а у Менеджеров будет небольшая задержка при работе с панелью управления сайта, что является некритическим.

Для создания и настройки пулов следует внести необходимые параметры в конфигурационный файл: /etc/php/7.4/fpm/pool.d/www.conf где настроить сокеты для каждой из частей веб-ресурса. Например, для фронтенда запись в файле будет выглядеть следующим образом:

frontend [frontend] listen = /var/run/php-fpm-frontend.sock user = www-data group = www-data listen.owner = www-data listen.group = www-data pm = static pm.max_children = 7

Здесь использовано сравнительно небольшое значение параметра max_children, что необходимо для оптимизации расхода ресурсов.

Мониторинг

При помощи статус-страницы FPM можно просмотреть много параметров работы. Она включается путем установки параметра pm.status_path в конфигурации пула. Можно получить вывод страницы в разных форматах – json, xml и других. 

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

https://localhost/fpm-status

Для формату json:

https://localhost/fpm-status?json

В частности, выводится следующая информация:

  • Общее количество принимаемых соединений;

  • Дата и время последнего запуска пула процессов;

  • Общее количество процессов;

  • Количество процессов, которые в настоящее время обрабатывают запросы;

  • Название пула;

  • ...........................................................................................

  • Подробная информация о каждом процессе:

    • Максимальный объем памяти, использованный последним запросом;

    • Длина тела последнего запроса;

    • Общее количество обслуживаемых запросов;

    • ................................................................................

    • Плоский процесс.

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

Выводы

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

  • Снизить CPU-нагрузку;

  • Организовать эффективное кэширование на уровне кода и данных;

  • Установите оптимальный режим работы препроцессора.

Обеспечить это может только интеграцией технологий: OPcache + APCu + Redis + FPM, которые мы кратко рассмотрели.

Подписывайтесь на наш телеграмм-канал https://t.me/freehostua, чтобы быть в курсе новых полезных материалов

Смотрите наш канал Youtube на https://www.youtube.com/freehostua.

Мы в чем ошиблись, или что-то пропустили?

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

Дата: 12.07.2025
Автор: Александр Ровник
Голосование

Авторам статьи важно Ваше мнение. Будем рады его обсудить с Вами:

comments powered by Disqus
navigate
go
exit
Спасибо, что выбираете FREEhost.UA