Навигация
- 2.1 Общие положения оптимизации на уровне фреймворка
- 2.2 Настройка конфигурации Yii2
- 2.3 Кэширование в Yii2
- 2.4 Эффективная работа с Active Record
2.1 Общие положения оптимизации на уровне фреймворка
Оптимизация производительности веб-приложения на уровне фреймворка является важным элементом повышения общей производительности веб-приложения. Фреймворк Yii2 предоставляет разработчикам широкий набор встроенных инструментов, призванных снизить нагрузку на сервер, ускорить обработку запросов и улучшить отзывчивость приложения без значительных изменений структуры приложения.
В книге Yii2 Application Development Cookbook оптимизации производительности приложения посвящена отдельная глава («Performance Tuning»)1. В указанной главе рассмотрены следующие способы оптимизации: 1) следование лучшим практикам; 2) ускорение обработки сессий; 3) использование зависимостей и цепочек кэша; 4) профилирование приложения с помощью Yii; 5) использование HTTP-кэширования; 6) объединение и минимизация ресурсов; 7) запуск Yii2 на HHVM.
Приведенный перечень способов оптимизации производительности веб-приложений, разработанных с использованием фреймворка Yii2, не является исчерпывающим. В данной главе рассмотрим основные методы оптимизации, реализуемые средствами Yii2. Предлагаем следующую классификацию таких способов: настройка конфигурации (глава 2.2); кэширование (глава 2.3); эффективная работа с Active Record (глава 2.4). В главе 2.5 настоящей ВКР рассмотрим на практике эффективность оптимизации производительности приложения на примере минификации и объединения CSS/JS через Asset Bundle.
2.2 Настройка конфигурации Yii2
Настройка конфигурации фреймворка Yii2 может воздействовать на производительность приложения. В рамках настоящей ВКР рассмотрим следующие способы настройки конфигурации Yii2: 1) включение продакшн-режима; 2) ускорение обработки сессий; 3) минификация и объединение CSS/JS через Asset Bundle.
2.2.1 Включение продакшн-режима
Yii2 поддерживает два основных режима: 1) dev – для разработки; 2) prod – для эксплуатации. В режиме разработки включены: подробные сообщения об ошибках; логирование; профилирование; дополнительные проверки. В продакшн-режиме все эти функции отключаются или минимизируются, что снижает накладные расходы и ускоряет работу приложения.
Когда параметр YII_DEBUG установлен в значение false, Yii отключает логирование уровня трассировки и использует меньше кода обработки ошибок. Кроме того, если YII_ENV установлен в значение «prod», то приложение не будет загружать панель отладки приложения Yii2 (которая также потребляет ресурсы). Отладочная панель собирает большое количество служебной информации: SQL-запросы; время выполнения; данные сессий; логи. В рабочей среде она не нужна и создает лишнюю нагрузку. По этим причинам для оптимизации производительности приложения в литературе рекомендуется включение продакшн-режима2.
2.2.2 Ускорение обработки сессий
В большинстве случаев стандартная обработка сессий в PHP вполне подходит. Существует как минимум две возможные причины, по которым может потребоваться изменение способа обработки сессий: 1) при использовании нескольких серверов необходимо иметь общее хранилище сессий для обоих серверов; 2) сессии PHP по умолчанию используют файлы, поэтому максимально возможная производительность ограничена дисковым вводом-выводом; 3) сессии PHP по умолчанию блокируют одновременное хранение сессий.
Для ускорения обработки сессий в литературе рекомендуется, например, добавить в файл /config/web.php следующую конфигурацию3:
'session' => [
'class' => \yii\web\CacheSession::class,
'cache' => 'sessionCache',
],
'sessionCache' => [
'class' => \yii\caching\MemCache::class,
],
2.2.3 Минификация и объединение CSS/JS через Asset Bundle
Фронтенд-ресурсы также влияют на производительность. Asset Bundle позволяет: объединять файлы CSS и JavaScript; минифицировать код; уменьшать количество HTTP-запросов; ускорять загрузку страниц. Особенно важно для мобильных пользователей и медленных сетей. Если веб-страница содержит много файлов CSS и/или JavaScript, она будет открываться очень медленно, поскольку браузер отправляет большое количество HTTP-запросов для загрузки каждого файла в отдельных потоках.
Чтобы уменьшить количество запросов и соединений, мы можем объединить и сжать несколько файлов CSS/JavaScript в один или несколько файлов в производственном режиме, а затем включить эти сжатые файлы на страницу вместо исходных4. Основная проблема заключается в том, что веб-приложение иногда подключает много статичных файлов, например: bootstrap.css, site.css, jquery.js, yii.js, bootstrap.js. В этом случае браузер делает много HTTP-запросов, открывает много соединений, ждет загрузку каждого файла. По этим причинам страница открывается медленно.
В этом случае для увеличения скорости приложения, в режиме эксплуатации (prod) можно: объединить все CSS в один файл; объединить все JS в один файл; удалить лишние пробелы и комментарии (минификация). В итоге будет существенно уменьшено количество загружаемых файлов (вместо 5-20 файлов будет 1-2 файла).
Это один из ключевых способов ускорения фронтенда. Который позволит улучшить: время, когда пользователь впервые видит любой визуальный элемент страницы (First Paint или FP); время, когда сайт не только отобразился, но и начал реагировать на действия пользователя (Time to Interactive или TTI).
Assets в Yii2 – это статические ресурсы: CSS; JavaScript; шрифты; изображения. Yii управляет ими через AssetBundle. До оптимизации HTML содержит много подключений:
После оптимизации получаем два файла:
В результате: меньше запросов; быстрее загрузка; и, соответственно, улучшается производительность. В приведенном примере используется встроенный инструмент yii asset, который: собирает все зависимости AssetBundle; публикует файлы в /web/assets; объединяет их; минифицирует; создает конфигурацию для режима эксплуатации (prod).
Сначала выполняем команду (php yii asset/template assets.php), которая создает конфигурационный файл (assets.php) содержащий шаблон для управления ассетами. Затем выполняем саму процедуру минификации для которой предусмотрена специальная команда: php yii asset assets.php assets-prod.php.
И в качестве завершающего шага, в конфигурационном файле config/web.php добавляем следующие строки кода:
'assetManager' => [
'bundles' => YII_ENV_PROD
? require(__DIR__ . '/assets-prod.php')
: [],
],
Соответствующее изменение конфигурации позволит автоматически подключать минифицированные ресурсы только в режиме эксплуатации. В результате в режиме разработки (dev) у нас будут использоваться обычные файлы (которые можно будет удобно дорабатывать), а в режиме эксплуатации (prod) – объединенные (минифицированные), которые будут быстро загружаться. Помимо этого, инструмент yii asset: переписывает относительные пути; корректно переносит шрифты и статические ресурсы; учитывает зависимости между библиотеками.
2.3 Кэширование в Yii2
Кэширование – один из наиболее эффективных способов повышения производительности, который позволяет сохранять результаты ресурсоемких операций и повторно использовать их без повторного выполнения. В Yii2 реализована развитая система кэширования, поддерживающая различные типы хранилищ.
До тех пор, пока программы на языке РНР остаются сценариями, которые запускаются, выдают результаты и умирают, идея кэширования результатов вычислений будет оставаться актуальной. Математическое понятие мемоизации, реализованное на уровне среды выполнения языка, повсеместно используется в экосистеме РНР, и фреймворк Yii поддерживает широкий спектр техник кэширования для ускорения программы.
В Yii2 есть следующие четыре уровня кэширования: 1) кэширование запросов к базе данных; 2) кэширование фрагментов НТМL-страниц; 3) кэширование запроса к серверу целиком; 4) кэширование НТМL-отклика при помощи некоторых заголовков НТТР.
Перед тем как мы посмотрим на все эти возможности по очереди, нам нужно узнать, что в Yii2 есть специальный компонент кэширования, который лежит в основе этого механизма5.
2.3.1 Применение HTTP-кэширования
Вместо реализации кэширования только на стороне сервера фреймворк Yii2 представляет возможность использовать кэширование на стороне клиента с помощью специальных HTTP-заголовков6. Во фреймворке Yii2 предусмотрена возможность кэширования на стороне клиента через HTTP-заголовки: Last-Modified и ETag (то есть не серверный кэш как в Yii2 PageCache, а механизм браузера). Подробно оптимизация на стороне клиента рассмотрена в главе 4 настоящей ВКР.
Браузер хранит страницу, а также ее метаданные. Если страница не изменилась, то сервер не отправляет ее заново. Без HTTP-кэша каждое обновление страницы представляет собой выполнение следующей последовательности процедур: 1) браузер делает запрос; 2) сервер выполняет код; 3) генерирует HTML; 4) отправляет ответ 200 OK. Соответствующая последовательность процедур выполняется каждый раз при обращении к странице приложения, даже если ничего не изменилось. Если страница не изменилась, то сервер отвечает: «304 Not Modified» и не отправляет страницу. В этом случае экономятся время, трафик, нагрузка на сервер, CPU и БД. Рассмотрим особенности HTTP-заголовков Last-Modified и ETag.
Использование HTTP-заголовка Last-Modified (кэширование на основе времени изменения ресурса) реализуется с применением фильтра yii\filters\HttpCache. Для списка статей значение времени последнего изменения определяется следующим образом:
'lastModified' => function () {
return Article::find()->max('updated_at');
}
Данный подход означает, что страница считается измененной в случае изменения времени последнего обновления одной из статей. При первоначальном обращении к ресурсу сервер возвращает ответ со статусом 200 OK и заголовком Last-Modified, содержащим дату последнего изменения ресурса (Last-Modified: Thu, 21 Apr 2026 00:56:02 GMT). При повторном обращении к ресурсу клиент (браузер) передает серверу HTTP-заголовок If-Modified-Since, содержащий дату последнего известного изменения ресурса. После обновления контента изменяется время и по этой причине сервер снова отправляет полную страницу, а также новый Last-Modified.
Использование HTTP-заголовка ETag (кэширование на основе содержимого ресурса) применяется для отдельных страниц, например страницы конкретной статьи. Значение ETag формируется на основе содержимого ресурса с использованием следующей конструкции:
'etagSeed' => function () {
return serialize([$article->title, $article->text]);
}
В данном случае сервер формирует уникальный идентификатор ресурса на основе его содержимого. При первом обращении к ресурсу сервер возвращает ответ со статусом 200 OK и заголовком ETag. При повторном запросе клиент передает серверу заголовок If-None-Match, содержащий ранее полученное значение ETag.
Если содержимое ресурса не изменилось, сервер возвращает ответ со статусом 304 Not Modified без тела сообщения. В случае изменения статьи формируется новое значение ETag, и сервер возвращает обновленный ресурс со статусом 200 OK и новым значением заголовка.
Last-Modified работает по времени, быстрый (требует меньше нагрузки на CPU), однако зависит от времени и поэтому считается, что может быть менее точным при частых изменениях. ETag считается более точным, зависит от содержимого (если содержимое изменилось, то ETag тоже изменится), можно использовать применительно к конкретным (единичным) ресурсам, а не массово ко всем сразу, может работать без использования временных меток (даты и времени изменения файла), однако требует больших вычислительных ресурсов сервера.
2.3.2 DataCache, FragmentCache и PageCache
DataCache предназначен для кэширования произвольных данных – результатов вычислений, выборок из базы данных, конфигурационных данных и т. д. Yii2 поддерживает несколько драйверов кэширования в числе которых можно отметить следующие: FileCache, RedisCache, MemCache.
FileCache сохраняет данные в файлах на диске. Прост в настройке и не требует дополнительного ПО. Подходит для небольших проектов или сред с ограниченными ресурсами. Преимущества заключаются в простоте использования, отсутствии необходимости в сторонних сервисах, а также в устойчивости к перезапуску сервера. В числе недостатков можно отметить сравнительно низкую скорость при большом количестве файлов, а также нагрузку на файловую систему.
MemCache использует оперативную память сервера через службу Memcached и обеспечивает таким образом высокую скорость доступа. Преимущества заключаются в высокой производительности и минимальных задержках. По этим причинам, MemCache в наибольшей степени подходит для высоконагруженных систем. К недостаткам можно отнести то, что данные теряются при перезапуске сервера, а также требуется установка Memcached.
RedisCache работает через систему Redis, которая представляет собой высокопроизводительное in-memory хранилище. В перечне преимуществ можно отметить высокую скорость, поддержку сложных структур данных, а также возможность использования как кэша и как базы данных. В качестве недостатка можно указать только необходимость установки и администрирования Redis.
FragmentCache применяется для кэширования отдельных частей страницы (виджетов, блоков, списков данных). Это особенно полезно, если страница содержит как статический, так и динамический контент, например, список популярных товаров, боковая панель, меню, блок новостей. FragmentCache позволяет обновлять только изменяющиеся части страницы, не затрагивая остальную разметку.
PageCache кэширует целиком сформированную страницу. При повторном обращении пользователю возвращается готовый HTML без выполнения логики приложения. Может использоваться для страниц с редко меняющимся содержимым, публичных разделов сайта, страниц без персонализации. PageCache может значительно снизить нагрузку на сервер, особенно при большом количестве однотипных запросов.
2.3.3 Кэширование запросов Active Record
Active Record в Yii2 поддерживает кэширование результатов SQL-запросов. Это позволяет избежать повторных обращений к базе данных при выполнении одинаковых запросов. Соответствующий механизм кэширования работает следующим образом: 1) выполняется SQL-запрос; 2) результат сохраняется в кэше; 3) при повторном запросе данные извлекаются из кэша. Приведенный способ оптимизации особенно эффективен для справочников, неизменяемых данных, а также часто используемых выборок. Кэширование запросов существенно уменьшает нагрузку на базу данных и ускоряет отклик системы.
Выполнение сложных запросов (например, аналитических), агрегирование данных, формирование отчетов может занимать значительное время. Если такие запросы выполняются часто и данные изменяются редко, целесообразно использовать кэширование. В Yii2 реализована возможность кэширования результатов запросов на уровне приложения.
Пример кэширования результата:
$stats = Yii::$app->cache->getOrSet('salesStats', function () {
return (new \yii\db\Query())
->select(['COUNT(*) AS total', 'SUM(amount) AS sum'])
->from('orders')
->where(['status' => 'completed'])
->one();
}, 600);
В приведенном примере кэш хранится 10 минут, после чего будет пересчитан.
Кэширование SQL на уровне БД-компонента:
$products = Yii::$app->db->cache(function ($db) {
return $db->createCommand(
'SELECT * FROM product WHERE status = 1'
)->queryAll();
}, 300);
Преимущества кэширования заключаются в снижении нагрузки на базу данных, ускорении отклика приложения, а также повышении устойчивости при пиковых нагрузках.
2.3.4 Использование зависимостей кэша и цепочек кэширования
При разработке веб-приложений часто возникают ситуации, при которых кэшировать данные на определенный период времени нецелесообразно, поскольку соответствующие данные могут измениться в любой момент. Ключевая проблема в этом случае заключается в том, что если закэшировать страницу, например, на 1 год, то данные не обновятся, даже если: 1) добавилась новая операция; 2) изменилась статья; 3) изменился баланс.
Зависимость кэша («Cache Dependency») представляет собой условие, при котором кэш считается устаревшим. При первом создании кэша сохраняется сама страница и контрольное значение зависимости. При повторном запросе: зависимость проверяется снова; если значение изменилось, то пересоздается кэш, а если нет, то используется старый кэш.
Цепочка кэширования («Caching Chain») – это объединение нескольких зависимостей. Кэш будет обновлен, если изменится хотя бы один из элементов этой цепочки. В литературе, рассматривается пример одновременного использования «DbDependency» и «TagDependency»7. DbDependency – при добавлении новой записи в таблицу, кэш обновится автоматически. TagDependency – зависимость по тегу (необходимо инвалидировать вручную, например, при сохранении текста статьи). Поддержка в Yii2 зависимостей кэша и цепочек кэширования позволяет кэшировать всю страницу и при этом всегда получать актуальные данные при обновлении.
2.4 Эффективная работа с Active Record
Active Record представляет собой удобный, но ресурсоемкий инструмент работы с базой данных. При неправильном использовании этот объект может стать узким местом в производительности веб-приложения. При работе со связями между моделями используются два основных подхода загрузки данных: отложенная загрузка (Lazy Loading) и жадная загрузка (Eager Loading).
Отложенная загрузка, подразумевает, что связанные данные загружаются только при первом обращении к ним. Такой подход может приводить к проблеме N+1-запросов, когда для каждой основной записи выполняется отдельный запрос к базе данных, что замедляет работу приложения при большом объеме данных.
В отличие от этого подхода жадная загрузка позволяет заранее загружать связанные данные одним запросом с использованием JOIN. Это снижает количество SQL-запросов и сокращает время выполнения операций, делая нагрузку на сервер более предсказуемой. Благодаря этому подходу достигается более высокая производительность при работе со списками и таблицами данных.
Поэтому для отображения больших объемов связанных данных в таблицах и списках часто используется именно жадная загрузка. Такой подход позволяет ускорить работу приложения, а также сократить количество избыточных запросов к базе данных.
При работе с большими таблицами загрузка всех записей сразу в память может привести к ее переполнению и падению приложения. Чтобы избежать таких проблем, применяются методы batch() и each(), позволяющие обрабатывать данные частями. Метод batch() возвращает наборы записей заданного размера, тогда как each() перебирает записи по одной, загружая их пакетами.
Использование этих методик позволяет существенно экономить оперативную память и обеспечивает возможность обработки большого количества данных без перегрузки системы. Такой подход позволяет создавать более устойчивые к большим нагрузкам веб-приложения, а также эффективно работать с большими объемами данных.
Особое значение это имеет при выполнении фоновых задач, миграции данных, генерации отчетов, а также при импорте и экспорте информации. Использование пакетной обработки данных в этих сценариях обеспечивает стабильность работы и оптимальное использование ресурсов системы.
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Сафронов М. Разработка веб-приложений в Yii 2 – М.: ДМК Пресс, 2015. ↩
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Bogdanov A., Eliseev D. Yii2 Application Development Cookbook. – Packt Publishing, 2016. ↩
Coding is used in almost all aspects of life and work now, be it directly or indirectly. It’s not just for companies in the tech sector. “An increasing number of businesses rely on computer code,
Coding is used in almost all aspects of life and work now, be it directly or indirectly. It’s not just for companies in the tech sector. “An increasing number of businesses rely on computer code,
Coding is used in almost all aspects of life and work now, be it directly or indirectly. It’s not just for companies in the tech sector. “An increasing number of businesses rely on computer code,