Ваш проект на Yii2 внезапно начал тормозить, когда пользователей стало больше сотни? Или вы только планируете запуск, который должен выдержать тысячи запросов в секунду? Тогда эта статья для вас. Мы разберём, как превратить обычное приложение на Yii2 в высоконагруженную систему без боли и лишних костылей.

Digital-студия WNDER

Почему Yii2 — не тормоз, а просто требует настройки

Многие думают: «Yii2 — это для средних проектов, для высоких нагрузок нужен Go или Node.js». Это заблуждение. Yii2 — это фреймворк, который может работать быстро, если понимать, как он устроен. Он не медленный, он просто «умный»: по умолчанию делает много лишнего, чтобы разработчику было удобно. Но когда приходит нагрузка, эту «умность» нужно отключать.

Представьте, что вы едете на внедорожнике по трассе. Комфортно, но бензин жрёт. А если нужно обогнать всех — включаете спортивный режим. Вот и с Yii2 так же: есть режим «разработка», а есть «продакшн под нагрузкой».

Кэширование: ваш главный союзник

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

Первый уровень — кэш данных. Это когда вы сохраняете результат тяжёлого запроса в Redis или Memcached. Например, вместо того чтобы каждый раз считать количество товаров в категории, вы кэшируете это число на 5 минут.


$count = Yii::$app->cache->get('category_count_' . $categoryId);
if ($count === false) {
    $count = Product::find()->where(['category_id' => $categoryId])->count();
    Yii::$app->cache->set('category_count_' . $categoryId, $count, 300);
}
return $count;

Второй уровень — кэш фрагментов. Если у вас есть блок «Популярные товары», который одинаков для всех пользователей, кэшируйте его целиком.


beginCache('popular_products', ['duration' => 3600])): ?>
    
endCache(); endif; ?>

Третий уровень — HTTP-кэш. Отдавайте статику с заголовками Cache-Control и ETag. Браузер сам будет кэшировать картинки и стили, не дёргая ваш сервер.

Правило: Никогда не кэшируйте данные без срока жизни. «Вечный» кэш — это мина замедленного действия. Всегда ставьте TTL (время жизни) хотя бы 5–10 минут.

Работа с базой данных: где чаще всего рвётся

База данных — это узкое место 90% высоконагруженных приложений. И Yii2 даёт вам инструменты, чтобы это место расширить.

1. Избегайте N+1 запросов. Это когда вы в цикле делаете запрос для каждой записи. Используйте жадную загрузку (with).


// Плохо: 100 запросов
$orders = Order::find()->all();
foreach ($orders as $order) {
    echo $order->user->name;
}

// Хорошо: 2 запроса
$orders = Order::find()->with('user')->all();
foreach ($orders as $order) {
    echo $order->user->name;
}

2. Используйте репликацию. Настройте master-slave: запись идёт на master, чтение — на slave. В Yii2 это делается в конфиге компонента db.


'db' => [
    'class' => 'yii\db\Connection',
    'masters' => [
        ['dsn' => 'mysql:host=master;dbname=app'],
    ],
    'slaves' => [
        ['dsn' => 'mysql:host=slave1;dbname=app'],
        ['dsn' => 'mysql:host=slave2;dbname=app'],
    ],
],

3. Индексы — это не роскошь. Проверьте EXPLAIN для каждого тяжёлого запроса. Если видите «Using filesort» или «Using temporary» — добавьте индекс.

Вот таблица, которая поможет быстро понять, какой тип кэша или оптимизации использовать в разных ситуациях:

Проблема Решение Инструмент Yii2
Много одинаковых запросов к БД Кэш данных Yii::$app->cache
Тяжёлые выборки с JOIN Репликация + индексы db->masters/slaves
Долгие операции (отправка почты, генерация отчётов) Очереди yii\queue\Queue
Медленный рендеринг страниц Кэш фрагментов beginCache/endCache

Очереди и фоновые задачи: не заставляйте пользователя ждать

Если пользователь нажал «Отправить заказ», он не должен ждать, пока вы сформируете PDF, отправите письмо и обновите статистику. Всё это нужно делать в фоне.

Yii2 имеет официальное расширение yii2-queue. Оно поддерживает Redis, RabbitMQ, AMQP, базы данных. Настройка проста:


// config/main.php
'components' => [
    'queue' => [
        'class' => 'yii\queue\redis\Queue',
        'hostname' => 'localhost',
        'port' => 6379,
        'database' => 0,
    ],
],

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


Yii::$app->queue->push(new SendOrderEmailJob([
    'orderId' => $order->id,
]));

А воркер (отдельный процесс) будет разгребать эти задачи. Это позволяет обрабатывать тысячи заказов, не блокируя веб-сервер.

Лайфхак: Запускайте несколько воркеров параллельно. Если у вас 4 ядра, запустите 4 процесса. Но следите, чтобы задачи не конфликтовали за одни и те же данные. Используйте блокировки (mutex).

Профилирование и мониторинг: без цифр вы слепы

Нельзя оптимизировать то, что не измеряешь. В Yii2 есть встроенный профайлер, но для продакшна он не подходит — слишком тяжёлый. Используйте Xdebug или Blackfire для локальной разработки, а на сервере — APM-системы (New Relic, Tideways).

Как минимум, включите логирование медленных запросов в MySQL:


// В конфиге db
'enableLogging' => YII_DEBUG,
'enableProfiling' => YII_DEBUG,

А для продакшна настройте slow query log на уровне MySQL (long_query_time = 1). Все запросы дольше секунды должны попадать в лог и разбираться.

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


'db' => [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=app',
    'enableSchemaCache' => true,
    'schemaCacheDuration' => 3600,
    'schemaCache' => 'cache',
],

Что в итоге: короткое резюме

Высоконагруженное приложение на Yii2 — это не магия, а набор простых правил. Кэшируйте всё, что можно. Избегайте N+1 запросов. Используйте репликацию и очереди. Следите за медленными запросами. И не забывайте про мониторинг.

Если вы только начинаете — внедряйте эти практики с самого начала. Если у вас уже есть проект — начните с профилирования, найдите узкое место и устраните его. Yii2 выдержит и миллион запросов в день, если подойти с умом.

Помните: производительность — это не разовое действие, а процесс. Постоянно измеряйте, оптимизируйте и тестируйте.

Студия WNDER