Ваш сайт на PHP тормозит, когда приходит много посетителей? Сервер падает, а пользователи уходят? Знакомая боль. В этой статье я расскажу, как выжать максимум из PHP-приложения, чтобы оно выдерживало тысячи запросов в секунду. Без магии, только проверенные техники и примеры кода.
Почему 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);
Оптимизация работы с базой данных
База данных — часто самое узкое место. Вот несколько приёмов, которые реально работают.
- Индексы. Если вы часто ищете по полю — добавьте индекс. Но не увлекайтесь: каждый индекс замедляет вставку и обновление.
- 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 и сервер. Пишите эффективный код, используйте очереди для тяжёлых задач и постоянно мониторьте производительность. Не пытайтесь оптимизировать всё сразу — двигайтесь итеративно, измеряя результат на каждом шаге.
Помните: даже небольшое улучшение на каждом уровне даёт заметный прирост в целом. Удачи!



