Ваш сайт на PHP тормозит, когда приходит много посетителей? Сервер падает, а пользователи уходят? Знакомая боль. В этой статье я расскажу, как выжать максимум из PHP-приложения, чтобы оно выдерживало тысячи запросов в секунду. Без магии, только проверенные техники и примеры кода.

Digital-студия WNDER

Почему PHP может тормозить

PHP — это интерпретируемый язык. Каждый раз, когда пользователь заходит на сайт, сервер выполняет кучу действий: читает код, компилирует его в байт-код, выполняет, подключает базы данных, файлы, внешние API. При высокой нагрузке это превращается в кашу. Но есть способы ускорить каждый этап.

Главные враги производительности: медленные запросы к БД, избыточные вычисления, отсутствие кеша, неоптимальный код и плохая конфигурация сервера. Давайте разбираться по порядку.

Кеширование — ваш лучший друг

Кеш — это как холодильник. Вместо того чтобы каждый раз бегать в магазин (базу данных), вы достаёте готовое из холодильника. Быстро и удобно. В PHP есть несколько уровней кеширования.

1. OPcache. Это встроенный кеш байт-кода. PHP не будет компилировать скрипты заново при каждом запросе. Включите его в php.ini:

opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60

2. Кеш данных. Используйте Memcached или Redis. Например, сохраняйте результаты тяжёлых запросов на 5 минут. Пример на PHP с Redis:

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'user_profile_' . $userId;
$data = $redis->get($key);
if ($data === false) {
    $data = $db->query('SELECT * FROM users WHERE id = ?', [$userId])->fetch();
    $redis->setex($key, 300, json_encode($data));
}
return json_decode($data, true);
Правило: Если данные не меняются каждую секунду — кешируйте их. Даже 5 секунд кеша могут снизить нагрузку на БД в разы.

Оптимизация работы с базой данных

База данных — часто самое узкое место. Вот несколько приёмов, которые реально работают.

  • Индексы. Если вы часто ищете по полю — добавьте индекс. Но не увлекайтесь: каждый индекс замедляет вставку и обновление.
  • JOIN вместо множества запросов. Один сложный запрос лучше, чем десять простых в цикле.
  • Лимитируйте выборку. Не делайте SELECT *, выбирайте только нужные поля. И всегда используйте LIMIT.
  • Подготовленные запросы. Они не только защищают от SQL-инъекций, но и ускоряют повторяющиеся запросы.

Пример плохого и хорошего кода:

// Плохо: запрос в цикле
foreach ($userIds as $id) {
    $user = $db->query("SELECT * FROM users WHERE id = $id")->fetch();
    // ...
}

// Хорошо: один запрос с IN
$ids = implode(',', array_map('intval', $userIds));
$users = $db->query("SELECT id, name, email FROM users WHERE id IN ($ids)")->fetchAll();

Код: пишем быстро и чисто

Даже идеальный сервер не спасёт, если код написан неэффективно. Вот несколько привычек, которые стоит выработать.

  • Избегайте лишних вычислений в циклах. Выносите count($array) за цикл, если массив не меняется.
  • Используйте встроенные функции. Они написаны на C и работают быстрее, чем самописные циклы на PHP.
  • Не злоупотребляйте магическими методами. __get, __set, __call замедляют выполнение.
  • Отключайте отладку в продакшене. Xdebug сильно тормозит.

Пример оптимизации цикла:

// Медленно
for ($i = 0; $i < count($items); $i++) { ... }

// Быстро
$count = count($items);
for ($i = 0; $i < $count; $i++) { ... }

Ещё один важный момент — автозагрузка классов. Используйте Composer с оптимизированным автозагрузчиком (composer dump-autoload -o). Это уменьшит количество файловых операций.

Настройка сервера и PHP-FPM

PHP-FPM — это менеджер процессов, который обрабатывает запросы. Его настройки напрямую влияют на производительность. Вот ключевые параметры:

Параметр Описание Рекомендация
pm.max_children Максимум дочерних процессов Зависит от RAM: (общая память - память ОС) / память на процесс
pm.start_servers Сколько процессов запускать сразу Обычно 2-4
pm.min_spare_servers Минимум свободных процессов 2-4
pm.max_spare_servers Максимум свободных процессов 4-8
pm.max_requests Число запросов до перезапуска процесса 500-1000 (борьба с утечками памяти)

Также используйте Nginx вместо Apache — он лучше держит статику и проксирует запросы к PHP-FPM. И не забудьте про gzip-сжатие и HTTP/2.

Лайфхак: Включите opcache.validate_timestamps=0 в продакшене. Это отключит проверку изменений файлов и даст прирост производительности. Но не забудьте сбрасывать OPcache при деплое.

Асинхронность и очереди

Если приложение выполняет долгие задачи (отправка email, генерация отчётов), не заставляйте пользователя ждать. Используйте очереди. Например, RabbitMQ или Redis-очереди. PHP может отправлять задачи в очередь, а воркеры обрабатывать их в фоне.

Пример с Redis-очередью:

// Отправка задачи в очередь
$redis->lPush('email_queue', json_encode(['to' => $email, 'subject' => 'Hello']));

// Воркер (запускается отдельно)
while (true) {
    $task = $redis->rPop('email_queue');
    if ($task) {
        $data = json_decode($task, true);
        // отправка email
    }
    usleep(100000); // 0.1 сек
}

Для асинхронности внутри одного запроса можно использовать библиотеки вроде ReactPHP или Swoole. Но это требует переписывания кода. Начните с очередей — это проще и эффективнее.

Мониторинг и профилирование

Нельзя оптимизировать то, что не измеряешь. Используйте инструменты:

  • Xdebug + KCachegrind — для профилирования в разработке.
  • Blackfire.io — для продакшена (осторожно, может замедлять).
  • New Relic или Tideways — мониторинг в реальном времени.
  • Логи медленных запросов MySQL. Включите slow_query_log и анализируйте.

Также следите за метриками: время ответа, количество запросов в секунду, использование CPU и памяти. Если видите, что какой-то скрипт выполняется долго — оптимизируйте его в первую очередь.

Что в итоге

Оптимизация PHP для высоких нагрузок — это комплекс мер. Начните с кеширования (OPcache, Redis), приведите в порядок запросы к БД, настройте PHP-FPM и сервер. Пишите эффективный код, используйте очереди для тяжёлых задач и постоянно мониторьте производительность. Не пытайтесь оптимизировать всё сразу — двигайтесь итеративно, измеряя результат на каждом шаге.

Помните: даже небольшое улучшение на каждом уровне даёт заметный прирост в целом. Удачи!

Студия WNDER