45Производительность
Чек-лист оптимизации — production-режим и opcache, кэш схемы и запросов, оптимизация автозагрузчика Composer, сборка ассетов, сессии в БД/кэше, тяжёлые выборки (asArray, batch, with), логирование и профилирование, где искать узкие места.
Yii быстр по умолчанию, но большинство «медленных сайтов» — это выключенный кэш, включённый debug и N+1 запросов. Ниже — порядок действий: сначала бесплатные переключатели, потом кэширование, потом профилирование и точечные правки.
1. Бесплатные переключатели#
- Production-режим: в
index.phpYII_DEBUG = false,YII_ENV = 'prod'(или не задавать — умолчания именно такие). Отладчик и Gii не подключать. - OPcache:
opcache.enable=1,opcache.validate_timestamps=0(сбрасывать при деплое),opcache.memory_consumption=256. Самый большой выигрыш. - Автозагрузчик:
composer install --no-dev --optimize-autoloader(илиdump-autoload -o --classmap-authoritative). - Кэш схемы БД:
'enableSchemaCache' => true, 'schemaCacheDuration' => 3600вdb— иначе AR читает структуру таблиц каждым запросом. Сбрасывать./yii cache/flush-schemaпосле миграций. - Логи:
traceLevel => 0в production (трассировка стека дорогая),flushInterval/exportIntervalпобольше, только нужныеlevels/categories. - Xdebug выключен на production.
2. Кэширование#
- Данные —
getOrSet()с зависимостями (кэширование данных); хранилище — APCu для одного сервера, Redis/Memcached для нескольких. - Запросы —
Query::cache(),Connection::cache(); схема — выше. - Фрагменты/страницы/HTTP — кэширование вывода.
- RBAC:
authManager.cache, URL-правила:urlManager.cache(по умолчанию используют компонентcache). - Переводы
PhpMessageSourceкэшируются opcache сами;DbMessageSource—cachingDuration.
3. Сессии#
Файловые сессии на одном сервере — нормально; при нескольких серверах или тысячах пользователей:
// или CacheSession с Redis/Memcached
'session' => ['class' => 'yii\web\DbSession', 'sessionTable' => 'session'],Не открывать сессию, когда не нужно (например, для API: enableSession => false); сборка мусора — gcProbability. Кэшированная сессия быстрее, но теряется при рестарте кэша.
4. База данных#
- Индексы по столбцам из
where,join,orderBy; смотрите EXPLAIN в панели Database отладчика. - N+1:
with()для связей в списках;joinWith('rel', false)+with(), если нужен фильтр по связи. asArray()для больших выборок только на чтение;select()только нужных столбцов;batch()/each()для экспорта.count()дорог на больших таблицах — кэшируйте или храните счётчик (updateCounters).- Массовые операции —
updateAll()/deleteAll()/batchInsert()вместо цикловsave(). - DAO там, где AR избыточен (агрегаты, отчёты).
- Реплики:
slavesв соединении — чтение с реплик, запись в мастер. - Постоянные соединения (
attributes => [PDO::ATTR_PERSISTENT => true]) — с осторожностью.
5. Ассеты и фронтенд#
./yii asset/template assets.php # шаблон конфигурации сборки
./yii asset assets.php config/assets-prod.phpКоманда объединяет и минифицирует CSS/JS бандлов (нужны closure-compiler/yuicompressor или свои jsCompressor/cssCompressor); результат подключается через assetManager.bundles. Плюс: appendTimestamp => true (кэш браузера), linkAssets => true (симлинки вместо копирования), отдача статики веб-сервером с gzip/brotli и заголовками кэша, CDN для jquery/bootstrap через переопределение бандлов.
6. Код приложения#
- Не вызывайте тяжёлое в
init()/конструкторах компонентов, которые создаются на каждом запросе; ленивые компоненты создаются только при обращении — не тяните их вbootstrapбез нужды. Html::encodeдешевлеHtmlPurifier::process— чистите HTML при сохранении, а не при показе (или кэшируйте).Yii::t()с intl быстрее без — но и файлы переводов не должны быть гигантскими.- Widgets/
renderв цикле — заменить на одинListView/строковую сборку. Yii::$app->formatterдля тысяч ячеек — предварительно вычислить формат один раз.- Уменьшайте
traceLevel, отключайтеprofilingв production.
7. Измерять, а не гадать#
Yii::beginProfile('block');
// …
Yii::endProfile('block');Панель Profiling и Database в отладчике показывают время по блокам и запросам. На production — yii\log\FileTarget с levels => ['profile'] на короткое время, APM (Blackfire, Xhprof, New Relic), медленные запросы в логах СУБД. ab/wrk/k6 для нагрузочного теста до и после изменений.
Чек-лист перед выкладкой#
, debug/gii выключены
включён, validate_timestamps=0
--no-dev -o
и cache не DummyCache/FileCache на нескольких серверах
в БД/Redis при нескольких серверах
проверен в панели Database на ключевых страницах
собраны, appendTimestamp, gzip на сервере
только error/warning, traceLevel=0
enableSchemaCache => true — иначе схема таблицы читается при каждом запросе.
Включаются трассировка, профилирование и подробные логи, что заметно замедляет каждый запрос.
Открыть панель Database отладчика: десятки одинаковых SELECT … WHERE id = ? — признак; лечится with().
Объединяет и минифицирует CSS/JS бандлов по конфигурации в единые файлы для production.
При нескольких веб-серверах (общий доступ) или очень большом числе сессий.