OpCache увеличивает производительность PHP сохраняя уже скомпилированные скрипты PHP в общей памяти, таким образом уменьшая работу для PHP-FPM, которому приходится меньше выполнять загрузку, чтение и обработку PHP при поступлении новых запросов.
Workflow обработки новых запросов выглядит так:
Исходный код проекта —
Содержание
Enable OpCache
Редактируем php.ini
файл, в данном примере это будет /etc/php/7.3/fpm/php.ini
.
Добавляем в блок [opcache]
:
[opcache] opcache.enable = 1
Проверяем синтаксис и перезагружаем конфиги PHP-FPM:
Проверяем phpinfo()
:
Другой вариант — проверить через вызов opcache_get_status()
:
<?php var_dump(opcache_get_status()); //var_dump(opcache_get_configuration()); ?>
Или вывести сразу в JSON:
<?php //var_dump(opcache_get_status()); //var_dump(opcache_get_configuration()); echo json_encode(opcache_get_status()); ?>
Все допустимые функции —
Проверяем:
array(8) { ["opcache_enabled"]=> bool(true)
— окей, активен.
Заодно видим тут память — сейчас обсудим.
Ещё один вариант проверить настройки OpCache — с помощью CLI:
OpCache tunning
Все доступные параметры —
OpCache memory
Начнём с памяти.
Возвращаемся к нашему статусу:
Тут видим использование выделенной памяти, которой по умолчанию 128 мегабайт, см. opcache.memory_consumption
used_memory
– integer; bytes of memory usedfree_memory
– integer; bytes of memory available for cachewasted_memory
– integer; bytes of memory used by invalid or outdated code — OUDATED codecurrent_wasted_percentage
– float; The percentage of wasted opcode cache memory out of total memory available
Wasted memory тут — например, у вас есть код на сервере, который уже закеширован OpCache.
Потом вы задеплоили новый код, OpCache выполнил проверку timestamps, увидел, что скрипт изменился — значит память, выделенная под кеш предыдущей версии скрипта, становится wasted — «зря потраченной«, т.к. кеш уже не валиден.
Значение wasted memory сбрасывается, когда OpCache перезапущен, или когда % wasted паамяти достигает лимита max_wasted_percentage
.
Посчитаем: 101210560 занято кешем сейчас + 24263968 свободной + 8743200 wasted:
Наши 128 метров из
Избегайте OpCache reset
Проблема заключается в том, что если OpCache забьёт всю доcтупную память (лимит в memory_consumption
) и/или сработает лимит max_wasted_percentage
и/или max_accelerated_files
— то OpCache сбросит весь кеш, и начнёт заполнять его заново.
При этом, даже при условии, что memory_consumption
и max_accelerated_files
будут полностью заняты, но в max_wasted_percentage
лимит ещё не достигнут — OpCache не выполнит reset кеша, и все PHP-скрипты, которые ещё не добавлены в кеш, будут обрабатываться в обычном порядке, т.е. будут каждый раз комплироваться в PHP-FPM.
Кроме того, могут возникнуть проблемы с блокировкиами записи в кеш, т.к. множество запросов к скриптам одновременно начнут зановсить в кеш новые записи об этих скриптах.
Потому — мониторим opcache_get_status()
, и проверяем следующие значения:
- Если
cache_full
, т.е. занята вся память под кеш изmemory_consumption
, но при этом нет «restart_pending» или «restart_in_progress» вopcache_get_status()
— возможно, у вас слишком низкий уровеньmax_wasted_percentage
.
Сравните значенияmax_wasted_percentage
в конфиге иcurrent_wasted_percentage
вopcache_get_status()
Решение: увеличиваем значениеmemory_consumption
- Если
cache_full
== true, а значениеnum_cached_keys
==max_cached_keys
— у вас слишком много файлов в кеше.
При этом если среди них очень мало wasted данных — то резет кеша не будет вызыван, место под новые скрипты в кеше не появится, и новые файлы будут обрабатываться мимо кеширования вообще.
Решение: увеличитьmax_accelerated_files
- Если
cache_full
не появляется никогда, но OpCache постоянно рестартится — то у вас либо слишком много файлов в wasted состоянии в кеше, либоmax_waste_percentage
слишком низкий.
Решение: увеличитьmax_waste_percentage
Проверка рестартов
Самый удобный способ проверить рестарты — через тот же статус:
Тут:
oom_restarts
: рестарты по достижению лимита памяти —memory_consumption
hash_restarts
: рестарты по достижению лимитовmax_accelerated_files
start_time
: время запуска в UNIX epochlast_restart_time
: время последнего перезапуска в UNIX epoch
Прочие настройки
opcache.use_cwd
Полезно включить, что бы OpCache будет использовать полный путь к файлу скрипта при добавлении его в кеш — это поможет избежать коллизий, когда один и тот же скрипт загружается как единый объект кеша, хотя файлы принадлежат разным проектам на сервере.
Например — у нас целая пачка приложений на Yii, во всех используются аналогичные файлы, т.к. фреймворк один и тот же.
opcache.blacklist-filename
Сюда можно внести например файлы настроек, если они часто изменяются, и любые другие динамически создаваемые-обновляемые файлы.
opcache.revalidate_freq
Как часто в секундах проверять обновились ли файлы. Если задать в 0 — то проверка будет выполняться при каждом запросе к файлу (но см. validate_timestamps
).
opcache.validate_timestamps
Проверяем модицикацию файлов файлов с периодом, заданным в revalidate_freq
.
Если validate_timestamps
задан в ноль, то проверка не будет выполняться вообще — имеет смысл на Production-окружении, т.к. там код будет обновляться редко (но в таком случае требуется либо перепзапуск OpCache при каждом деплое/апдейте файлов, либо выполнять явный вызов reset()
).
opcache.max_accelerated_files
Уже упоминалась, но ещё раз — максимальное кол-во файлов, который OpCache будет хранить. Для лучшей производительности используйте просыте числа — 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987.
В идеале — должно быть больше, чем общее кол-во файлов всех проектов:
Тогда как лимит по дефолоту задан в 10000, а закешировано сейчас:
opcache.interned_strings_buffer
PHP использует Interned Strings для хранения данных типа string: для одинаковых строк создаётся одна переменная, которая хранит значение, а во всех остальных случаях — используется указатель (pointer) на эту переменную.
Вместо того, что бы создавать такую переменную для каждого процесса PHP-FPM — OpCache создаёт одну перменную для всех процессов, которые используют одну и ту же строку.
Значение задаётся в МБ, при этом оно будет отнято от значения из opcache.memory_consumption
.
opcache.file_cache
Добавляет кеширование в файл. Помогает при перезагрузке сервера или если вся shared память занята (см. /proc/sys/kernel/shmmax
).
opcache.enable_cli
Включает поддержку кеширования для PHP CLI. Имеет смысл когда, например, часть скриптов запукается из crontab
.
Config summary
Соберём всё вместе:
opcache.memory_consumption=164 // 164 MB памяти под кеш opcache.max_wasted_percentage=5 // при 5% wasted данных - будет выполнен reset всего кеша opcache.max_accelerated_files=7963 // при достижении максимального значения закешированных файлов - будет выполнен reset opcache.use_cwd=1 // использовать полный путь для создания униканого имени ключа opcache.revalidate_freq=0 // проверять не изменился ли файл при каждом запросе, не играет роли при validate_timestamps=0 opcache.validate_timestamps=0 // никогда не проверять изменился ли файл на диске, пока не будет выполнен reset() или перезагрузка сервера opcache.interned_strings_buffer=32 // 32 MB под strings-кеш opcache.file_cache=/data/opcache // файл(ы) кеша храним в /data/opcache opcache.enable_cli=0 // включить OpCache для PHP CLI
OpCache GUI — веб-интерфейс
Для более наглядного представления и работы с OpCache имеется много веб-интерфейсов, например opcache-gui
Загружаем файл:
Копируем его в каталог проекта, что бы получить доступ через NGINX:
Открываем в браузере:
Тестирование и графики
И графики потребления CPU (% Load Average сверху) и RAM (нижний график) на двух серверах API-фронтенда нашего бекенда.
Видим, что Load Average прыгал до 2000% без использования кеша, и всего 300-400% — с активным кешированием.
Аналогично память — 25-50% без кеширования, и 21-35% — с кешированием.
В целом — на этом всё.