Представьте: вы запустили крутой сервис, всё работает отлично. А потом приходит толпа пользователей — и сайт падает. Знакомо? Если нет, то вы либо новичок, либо уже умеете готовить приложения к высоким нагрузкам. В этой статье я расскажу, как на 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. Так быстрее и меньше памяти.

Правило: Всегда кешируйте данные, которые не меняются каждую секунду. Даже 5 секунд кеша снижают нагрузку на базу в разы. Используйте тэги кеша, чтобы инвалидировать данные группами.

Работа с базой данных: меньше запросов — больше пользы

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!',
]));

А обработчик будет выполняться в фоне, не блокируя ответ пользователю.

Лайфхак: Используйте очереди для всего, что не требует мгновенного результата. Отправка email, генерация PDF, парсинг данных — всё это должно быть асинхронным. Так вы освободите PHP-процессы для обработки реальных запросов.

Оптимизация сессий и статики

Сессии по умолчанию хранятся в файлах. Это медленно, особенно если у вас несколько серверов. Переключитесь на 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, а в медленных SQL-запросах или отсутствии индексов. Используйте EXPLAIN для анализа запросов.

Резюме

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

Что нужно запомнить:

  • Кешируйте всё, что можно кешировать
  • Избегайте N+1 запросов, используйте жадную загрузку
  • Тяжёлые задачи отправляйте в очередь
  • Храните сессии в Redis
  • Профилируйте и оптимизируйте только узкие места

Начните с малого — настройте кеширование и перепишите пару запросов. Увидите, как приложение начнёт летать. А если что-то пойдёт не так — вы уже знаете, где искать.

Студия WNDER