Представьте: вы запустили крутой сервис, всё работает отлично. А потом приходит толпа пользователей — и сайт падает. Знакомо? Если нет, то вы либо новичок, либо уже умеете готовить приложения к высоким нагрузкам. В этой статье я расскажу, как на Yii2 писать код, который не ляжет под напором 10 000 запросов в секунду. Без магии, только проверенные приёмы.
Почему Yii2 не тормозит сам по себе
Многие думают, что фреймворки медленные. Это миф. Yii2 — один из самых быстрых PHP-фреймворков. Он лениво загружает компоненты, использует кеширование и умеет работать с пулом соединений. Но если вы пишете код спустя рукава, даже самый быстрый фреймворк превратится в тормоз.
Главные враги производительности в Yii2:
- Ненужные запросы к базе в циклах
- Отсутствие кеширования
- Тяжёлые вычисления на лету
- Неоптимизированные Active Record запросы
Давайте разберёмся, как с этим бороться.
Кеширование — ваш лучший друг
Кеширование в Yii2 — это не просто галочка в настройках. Это целая философия. Если вы можете сохранить результат работы и отдать его без повторных вычислений — делайте это.
В Yii2 есть несколько типов кеширования:
| Тип кеша | Когда использовать | Пример |
|---|---|---|
| Файловый кеш | Для небольших проектов или когда нет доступа к Redis/Memcached | Кеширование списка категорий |
| Кеш в памяти (Redis) | Для высоконагруженных систем, где скорость критична | Сессии, данные пользователя, результаты сложных запросов |
| Кеш БД | Для данных, которые редко меняются | Настройки приложения |
Пример простого кеширования запроса:
$data = Yii::$app->cache->get('top_users');
if ($data === false) {
$data = User::find()
->select(['id', 'name', 'points'])
->orderBy(['points' => SORT_DESC])
->limit(10)
->asArray()
->all();
Yii::$app->cache->set('top_users', $data, 3600); // кеш на час
}
Обратите внимание: мы используем asArray() — это возвращает обычные массивы, а не объекты Active Record. Так быстрее и меньше памяти.
Работа с базой данных: меньше запросов — больше пользы
Active Record в Yii2 удобен, но он может порождать сотни запросов, если вы неаккуратны. Классическая проблема — N+1 запрос. Это когда вы в цикле получаете связанные данные для каждой записи.
Пример плохого кода:
$users = User::find()->all();
foreach ($users as $user) {
echo $user->profile->bio; // каждый раз новый запрос к таблице profile
}
Как исправить? Используйте жадную загрузку:
$users = User::find()->with('profile')->all();
foreach ($users as $user) {
echo $user->profile->bio; // один запрос на всех
}
Ещё один совет: не используйте Active Record для массовых операций. Если нужно обновить 10 000 записей, используйте Query Builder или прямые SQL-запросы.
Быстрое обновление через Query Builder:
Yii::$app->db->createCommand()
->update('user', ['status' => 1], 'last_login < :date', [':date' => '2023-01-01'])
->execute();
Это выполнится за доли секунды, в отличие от цикла с сохранением каждого объекта.
Очереди: не делайте всё сразу
Если ваше приложение должно отправлять письма, обрабатывать изображения или генерировать отчёты — не делайте это синхронно. Пользователь не будет ждать 10 секунд, пока письмо уйдёт.
В Yii2 есть официальное расширение yii2-queue. Оно поддерживает RabbitMQ, Redis, Beanstalk и даже базу данных. Настройка простая:
// config/queue.php
'components' => [
'queue' => [
'class' => \yii\queue\redis\Queue::class,
'redis' => 'redis', // компонент подключения к Redis
'channel' => 'queue',
],
],
Теперь любую задачу можно отправить в очередь:
Yii::$app->queue->push(new SendEmailJob([
'email' => 'user@example.com',
'subject' => 'Welcome!',
]));
А обработчик будет выполняться в фоне, не блокируя ответ пользователю.
Оптимизация сессий и статики
Сессии по умолчанию хранятся в файлах. Это медленно, особенно если у вас несколько серверов. Переключитесь на Redis или базу данных.
Настройка сессий через Redis:
'components' => [
'session' => [
'class' => 'yii\redis\Session',
'redis' => [
'hostname' => 'localhost',
'port' => 6379,
'database' => 0,
],
],
],
Статику (CSS, JS, изображения) отдавайте через CDN или nginx. Yii2 умеет собирать и минифицировать ресурсы через AssetBundle. Настройте их так, чтобы файлы кешировались на стороне клиента.
Пример AssetBundle для продакшена:
class AppAsset extends AssetBundle
{
public $basePath = '@webroot/assets';
public $baseUrl = '@web/assets';
public $css = [
'css/site.min.css', // минифицированная версия
];
public $js = [
'js/app.min.js',
];
public $depends = [
'yii\web\YiiAsset',
];
}
Не забудьте включить сжатие и кеширование на уровне веб-сервера.
Профилирование: найдите узкие места
Вы не можете оптимизировать то, что не измеряете. В Yii2 есть встроенный профилировщик — Yii Debug Toolbar. Он показывает время выполнения запросов, количество SQL-запросов, использование памяти.
Включите его в режиме разработки:
// config/web.php
if (YII_ENV_DEV) {
$config['modules']['debug'] = [
'class' => 'yii\debug\Module',
'allowedIPs' => ['127.0.0.1', '::1'],
];
}
А для продакшена используйте логирование медленных запросов. Например, через XHProf или Blackfire.io. Но даже простой анализ логов базы данных может показать, какие запросы тормозят.
Резюме
Высокая нагрузка — это не страшно, если вы готовы. Yii2 даёт все инструменты для создания быстрых приложений: кеширование, очереди, профилирование. Главное — не лениться и применять их правильно.
Что нужно запомнить:
- Кешируйте всё, что можно кешировать
- Избегайте N+1 запросов, используйте жадную загрузку
- Тяжёлые задачи отправляйте в очередь
- Храните сессии в Redis
- Профилируйте и оптимизируйте только узкие места
Начните с малого — настройте кеширование и перепишите пару запросов. Увидите, как приложение начнёт летать. А если что-то пойдёт не так — вы уже знаете, где искать.
Студия WNDER

