Performance Best-Practice Guide
При разработке веб-приложений с высокой нагрузкой обычно уделяется особое внимание мерам по повышению производительности и внесению изменений, которые делают приложение еще быстрее. Хорошо оптимизированный сайт привлекает больше трафика без дополнительных затрат и значительно улучшает пользовательский опыт. Для оптимизации производительности сайта ниже приведены рекомендуемые инструменты и конфигурации. В каждом разделе указаны результаты нагрузочного тестирования, выполненного с помощью утилиты командной строки ApacheBench, что позволяет сравнить среднее время загрузки страницы до и после применения изменений.
Перед началом тестирования важно отключить режимы разработки и отладки, установив следующие переменные окружения, чтобы воспроизвести сценарий продакшн-окружения:
APP_ENV=prod
APP_DEBUG=0
PIMCORE_DEV_MODE=0
PHP 8: Opcache и JIT-компилятор
Как известно, PHP — интерпретируемый язык, что означает: при выполнении PHP-скриптов интерпретатор каждый раз разбирает, компилирует и исполняет код (opcode). Это может приводить к лишним затратам процессорных ресурсов и времени выполнения. Здесь на сцену выходит расширение OPcache:
«OPcache повышает производительность PHP, сохраняя предварительно скомпилированный байткод скриптов в разделяемой памяти, тем самым устраняя необходимость загружать и разбирать скрипты при каждом запросе».
В PHP 8 была добавлена поддержка Just-In-Time компиляции (JIT), которая имеет большой потенциал для дополнительного значительного ускорения работы. Для функционирования JIT-компилятора необходимо, чтобы OPcache был включен.
По умолчанию JIT-компилятор в PHP 8 отключен. Чтобы включить его, добавьте следующую конфигурацию в файл php.ini:
opcache.enable=1 # включает OPcache для PHP-сервера
opcache.enable_cli=1 # включает OPcache для режима CLI
opcache.jit_buffer_size=256M # размер памяти JIT-буфера. Значение 0 отключает JIT-компилятор
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en
- До изменений (OPcache включен, JIT выключен)
Time taken for tests: 1.061 seconds
Time per request: 212.203 [ms] (mean)
Time per request: 10.610 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 65 182 41.2 205 225
Waiting: 64 182 41.3 205 224
Total: 66 183 41.0 205 225
- После изменений
Time taken for tests: 1.008 seconds
Time per request: 201.617 [ms] (mean)
Time per request: 10.081 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 65 175 36.9 181 241
Waiting: 65 174 36.9 180 240
Total: 66 175 36.7 181 241
Лучшие практики производительности Symfony
Поскольку Pimcore использует фреймворк Symfony для обработки backend-запросов, настоятельно рекомендуется следовать лучшим практикам производительности Symfony.
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en
- До изменений
Time taken for tests: 1.100 seconds
Time per request: 219.904 [ms] (mean)
Time per request: 10.995 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 2
Processing: 70 194 44.6 200 289
Waiting: 69 193 44.6 200 289
Total: 70 194 44.4 200 290
- После изменений
Time taken for tests: 1.042 seconds
Time per request: 208.337 [ms] (mean)
Time per request: 10.417 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 56 182 37.1 189 254
Waiting: 55 182 37.1 189 254
Total: 56 182 36.9 189 255
Предзагрузка Opcache
Предзагрузка Opcache была добавлена в PHP 7.4. Это функция, которая может значительно повысить производительность приложения за счет предварительной загрузки часто используемых файлов фреймворка и сохранения сгенерированного байткода в памяти.
Чтобы включить предзагрузку OPcache, настройте следующие параметры в файле php.ini:
opcache.preload_user=www-data # пользователь, под которым запускается PHP (PHP-FPM)
opcache.preload=/var/www/html/var/cache/prod/App_KernelProdContainer.preload.php # путь к файлу предзагрузки в директории /var/cache проекта
Примечание: при использовании предзагрузки с Pimcore значение memory_limit должно быть установлено как минимум 200M.
Также можно указать, какие классы должны или не должны быть предзагружены, используя теги сервисов Symfony container.preload и container.no_preload.
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en/shop/Products/Cars/Economy-Cars/Fiat-500~p104
- До изменений
Time taken for tests: 0.577 seconds
Time per request: 115.394 [ms] (mean)
Time per request: 5.770 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 35 99 23.1 112 127
Waiting: 33 97 23.1 109 125
Total: 35 99 22.9 112 127
- После изменений
Time taken for tests: 0.468 seconds
Time per request: 93.522 [ms] (mean)
Time per request: 4.676 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 32 80 17.8 79 111
Waiting: 30 78 17.7 76 110
Total: 33 81 17.6 79 111
Оптимизация MySQL / MariaDB
Pimcore использует паттерн Data Access Object (DAO) для отделения бизнес-логики от низкоуровневых операций доступа к данным. Эти операции доступа к данным играют ключевую роль в производительности функций Pimcore, которым требуется чтение и запись в базу данных. Поэтому важно настроить конфигурацию базы данных так, чтобы она была производительной и точно соответствовала требованиям вашего приложения.
Составные индексы MySQL
Если вы используете объекты данных (Data Objects) или пользовательские таблицы базы данных для хранения данных, рекомендуется оптимизировать эти таблицы с помощью составных индексов. Это позволяет оптимизатору запросов использовать составной индекс для запросов, которые соответствуют всем столбцам индекса, вместо полного сканирования таблицы.
Составные индексы для объектов данных можно настроить в определении класса в разделе General Settings.
Буферный пул MySQL
Буферный пул — это область в оперативной памяти, в которой InnoDB кэширует данные таблиц и индексов по мере обращения к ним. Основная задача буферного пула — сократить время ответа при извлечении данных.
Системную переменную сервера innodb_buffer_pool_size рекомендуется устанавливать в диапазоне 70–80% от общего объема доступной памяти на выделенном сервере базы данных, где используются только или преимущественно таблицы InnoDB.
Для выбора оптимального размера буферного пула ознакомьтесь с соответствующим обсуждением и обновите файл my.cnf:
[mysqld]
innodb_buffer_pool_size=5G # значение необходимо подобрать в зависимости от объема данных
- Также можно использовать скрипт Mysql Tuning для дополнительной оптимизации — mysqltuner.pl
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en/shop/Products/Cars/Economy-Cars~c547
- До изменений
Time taken for tests: 2.515 seconds
Time per request: 503.024 [ms] (mean)
Time per request: 25.151 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 124 453 116.0 457 727
Waiting: 121 448 115.3 453 724
Total: 124 454 115.9 458 727
- После изменений
Time taken for tests: 2.251 seconds
Time per request: 450.187 [ms] (mean)
Time per request: 22.509 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 123 401 83.0 413 542
Waiting: 120 397 82.5 408 539
Total: 123 401 82.8 413 542
Кэширование в Pimcore (Redis)
Pimcore активно использует кэширование для различных типов данных. Основной кэш — это чистый объектный кэш, в котором каждый элемент Pimcore (документ, ассет, объект данных) кэшируется в виде сериализованных объектов.
Этот основной кэш использует интерфейс Pimcore\Cache для хранения объектов. Pimcore\Cache применяет Pimcore\Cache\Core\CoreCacheHandler, который реализует логику кэширования Pimcore поверх реализации PSR-6 кэша с поддержкой тегов. Pimcore использует пул кэша Symfony pimcore.cache.pool, который можно настраивать в соответствии с требованиями проекта, однако крайне важно, чтобы этот пул поддерживал теги.
По умолчанию Pimcore использует подключение Doctrine и записывает данные в таблицы cache_items базы данных.
Также можно использовать доступные адаптеры кэша Symfony, однако Pimcore рекомендует использовать адаптер Redis для повышения производительности, с возможностью кластеризации и поддержкой failover.
Настройте адаптер Redis для Pimcore\Cache, используя следующие параметры:
# config/packages/cache.yaml
framework:
cache:
pools:
pimcore.cache.pool:
public: true
default_lifetime: 31536000 # 1 year
adapter: cache.adapter.redis_tag_aware
provider: 'redis://localhost'
Обратите внимание, что данные элементов добавляются в кэш при первом обращении к ним. Чтобы прогреть кэш после его очистки (например, после деплоя), выполните команду bin/console pimcore:cache:warming, чтобы пользователи получали оптимальную производительность даже при первом обращении к элементам.
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en/shop/Products/Cars~c390
- До изменений
Time taken for tests: 6.495 seconds
Time per request: 1299.007 [ms] (mean)
Time per request: 64.950 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 3
Processing: 386 1178 209.6 1222 1550
Waiting: 383 1169 208.5 1215 1539
Total: 386 1178 209.6 1222 1551
- После изменений
Time taken for tests: 5.560 seconds
Time per request: 1111.956 [ms] (mean)
Time per request: 55.598 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 2
Processing: 314 1021 196.1 1010 2106
Waiting: 312 1010 192.8 998 2080
Total: 315 1021 196.0 1011 2107
Полностраничный кэш Pimcore
Полностраничный кэш Pimcore позволяет кэшировать контент, возвращаемый действием контроллера. Благодаря этому один и тот же контент не требуется генерировать каждый раз при вызове одного и того же действия контроллера. Запрос, обработанный через MVC-фреймворк, кэшируется и сохраняется в пуле кэша, поэтому при последующих запросах ответ отдается напрямую из кэша.
Вы можете настроить кэширование выходных страниц в файле config.yaml, например:
pimcore:
full_page_cache:
enabled: true
lifetime: 120
exclude_cookie: 'pimcore_admin_sid'
exclude_patterns: '@^/test/de@'
Бенчмарки:
On running command, ab -n 100 -c 20 http://localhost/en
- До изменений
Time taken for tests: 0.704 seconds
Time per request: 140.810 [ms] (mean)
Time per request: 7.040 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 45 120 27.7 123 166
Waiting: 45 120 27.7 122 165
Total: 46 120 27.5 123 166
- После изменений
Time taken for tests: 0.646 seconds
Time per request: 129.298 [ms] (mean)
Time per request: 6.465 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 45 115 26.0 116 167
Waiting: 45 114 26.0 116 166
Total: 46 115 25.9 116 167
Важное примечание о сессиях
Используйте сессии только в случае реальной необходимости. Полностраничный кэш Pimcore определяет использование сессий в коде и при необходимости автоматически отключается.
Генератор статических страниц
Pimcore предоставляет сервис генерации статических страниц, который создает статические HTML-файлы на основе страниц (документов). Эти статические файлы могут отдаваться напрямую через Apache или Nginx, без прохождения полного MVC-цикла на PHP при фронтенд-запросе. Данная функция не рекомендуется для страниц с динамическим контентом, таких как списки новостей, страницы товаров и т.п., где контент часто меняется.
Включение генератора статических страниц для документа
Чтобы включить автоматическую генерацию статической страницы для документа, перейдите в Document -> Settings -> Static Page Generator. отметьте флаг enable, при необходимости задайте время жизни статической страницы (после истечения которого страница будет пересоздана) и сохраните документ.
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en/Magazine
- До изменений
Time taken for tests: 0.803 seconds
Time per request: 160.651 [ms] (mean)
Time per request: 8.033 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.6 0 15
Processing: 55 140 47.6 129 274
Waiting: 53 136 46.8 127 268
Total: 57 141 47.7 130 274
- После изменений
Time taken for tests: 0.033 seconds
Time per request: 6.686 [ms] (mean)
Time per request: 0.334 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 2 5 1.5 5 10
Waiting: 2 5 1.6 5 10
Total: 3 6 1.3 6 10
Кэширование внутри шаблонов
Pimcore предоставляет расширение Twig pimcore_cache для кэширования прямо внутри шаблонов. Оно используется для кэширования отдельных частей шаблона независимо от глобальной системы кэширования. Это может быть полезно для шаблонов, которые требуют большого количества вычислений или работают с большим объемом объектов, например навигации.
Вы можете кэшировать часть шаблона следующим образом:
{% set cache = pimcore_cache("main_navigation_cache", 60) %}
{% if not cache.start() %}
{% set navStartNode = document.getProperty('navigation_root') %}
{% if not navStartNode is instanceof('\\Pimcore\\Model\\Document') %}
{% set navStartNode = pimcore_document(1) %}
{% endif %}
{% set mainNavigation = app_navigation_data_links(document, navStartNode) %}
<div class="container">
...
{{
pimcore_render_nav(mainNavigation, 'menu', 'renderMenu', {
maxDepth: 2,
ulClass: {
0: 'navbar-nav menu-links ml-4 m-auto',
1: 'dropdown dropdown-menu',
'default': 'dropdown-menu dropdown-submenu'
}
})
}}
...
</div>
{% do cache.end() %}
{% endif %}
Подробнее о pimcore_cache можно прочитать здесь.
Бенчмарки:
При выполнении команды ab -n 100 -c 20 http://localhost/en/Magazine
- До изменений
Time taken for tests: 0.808 seconds
Time per request: 161.603 [ms] (mean)
Time per request: 8.080 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 48 137 35.8 135 207
Waiting: 45 133 35.4 130 204
Total: 49 137 35.7 135 207
- После изменений
Time taken for tests: 0.644 seconds
Time per request: 128.881 [ms] (mean)
Time per request: 6.444 [ms] (mean, across all concurrent requests)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 41 113 28.6 114 175
Waiting: 34 109 28.3 111 169
Total: 42 113 28.4 114 175
Кэш Varnish
Varnish Cache — это так называемый кэширующий reverse proxy, который размещается перед сервером и отвечает за доставку контента сайта. При первом запросе Varnish сохраняет копию ответа сервера в памяти. При последующих запросах ответ отдается напрямую из памяти, минуя сервер. Это приводит к более быстрому времени отклика, так как на стороне сервера не требуется дополнительная обработка.
Примечание: Pimcore отправляет корректные заголовки для Varnish, если включен полностраничный кэш.
Edge Side Includes (ESI)
Edge Side Includes — это разметка для управления фрагментами страницы, которые могут кэшироваться и объединяться в одну страницу. Эти фрагменты могут иметь собственные политики кэширования и использоваться на нескольких страницах.
Varnish поддерживает два основных ESI-тега:
esi:include
esi:remove
Symfony имеет встроенную поддержку ESI. Подробнее читайте здесь. Также можно узнать больше о Varnish.
Примечание: ESI-теги несовместимы с генератором статических страниц (Static Page Generator).
Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.