Ваш сайт работал отлично, пока пользователей было мало. Но стоило прийти тысяче человек одновременно — и всё зависло. Знакомая история? В этой статье разберём, как заставить PHP-приложение летать даже при высокой нагрузке. Без магии, только проверенные приёмы.
Почему PHP тормозит под нагрузкой
PHP — интерпретируемый язык. Каждый запрос заново выполняет скрипт, подключает файлы, создаёт соединения с базой. Когда запросов тысячи в секунду, накладные расходы становятся критичными.
Основные узкие места:
- Медленные SQL-запросы без индексов.
- Отсутствие кэширования.
- Избыточная загрузка файлов и библиотек.
- Синхронные операции, которые можно отложить.
Давайте разбираться по порядку.
1. Профилируйте, а не гадайте
Первое правило оптимизации: не трогайте код, пока не знаете, что именно тормозит. Используйте профайлеры — Xdebug, Blackfire или Tideways. Они покажут, какие функции съедают больше всего времени.
Пример простого профилирования с Xdebug:
// Включаем профайлер
xdebug_start_trace('/tmp/trace.log');
// Ваш код
$result = heavyFunction();
xdebug_stop_trace();
После анализа лога вы увидите, например, что 80% времени уходит на запрос к базе без индекса. Или на рекурсивную функцию, которую можно заменить циклом.
2. Кэширование: ваш главный союзник
Кэш — это как холодильник. Вместо того чтобы каждый раз бежать в магазин (базу данных), вы достаёте готовое. Чем меньше запросов к БД, тем выше скорость.
Уровни кэширования:
| Тип | Что кэшируем | Инструменты |
|---|---|---|
| OPcache | Скомпилированный байт-код PHP | Встроен в PHP |
| Данные | Результаты запросов, объекты | Redis, Memcached |
| Страницы | Готовый HTML | Varnish, Nginx FastCGI Cache |
Пример кэширования в Redis:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'user:123:profile';
$data = $redis->get($key);
if (!$data) {
$data = $db->query('SELECT * FROM users WHERE id = 123')->fetch();
$redis->setex($key, 3600, serialize($data));
} else {
$data = unserialize($data);
}
3. Оптимизация работы с базой данных
База — это сердце приложения. Если она тормозит, всё остальное бесполезно.
Что делать:
- Индексируйте поля, по которым идёт поиск и сортировка.
- Избегайте SELECT * — берите только нужные колонки.
- Используйте подготовленные запросы (prepared statements) для безопасности и скорости.
- Разбивайте сложные запросы на простые.
- Для больших таблиц применяйте партиционирование.
Пример плохого и хорошего запроса:
// Плохо: каждый раз новый запрос, нет индекса
$users = $db->query("SELECT * FROM users WHERE status = 'active'");
// Хорошо: используем подготовленный запрос и индекс
$stmt = $db->prepare("SELECT id, name FROM users WHERE status = ?");
$stmt->execute(['active']);
$users = $stmt->fetchAll();
Не забывайте про соединения. Постоянные соединения (persistent connections) экономят время на установку связи с БД.
4. Асинхронность и очереди
Не всё нужно делать сразу. Отправка email, генерация отчётов, обработка изображений — эти задачи можно отложить. Используйте очереди: RabbitMQ, Redis, Beanstalkd.
Пример с Redis в качестве очереди:
// Постановка задачи в очередь
$redis->lPush('email_queue', json_encode(['to' => 'user@example.com', 'subject' => 'Hello']));
// В отдельном воркере
while (true) {
$job = $redis->brPop('email_queue', 30);
if ($job) {
$data = json_decode($job[1], true);
sendEmail($data['to'], $data['subject']);
}
}
Это разгружает веб-сервер и ускоряет ответ пользователю.
5. Настройка сервера и PHP-FPM
PHP-FPM — это менеджер процессов, который обрабатывает запросы. Его настройки напрямую влияют на производительность.
Ключевые параметры:
| Параметр | Рекомендация |
|---|---|
| pm | dynamic (для переменной нагрузки) или static (для стабильной) |
| pm.max_children | Зависит от RAM: (доступная память) / (память на процесс) |
| pm.start_servers | Обычно 2-4 |
| pm.min_spare_servers | 1-2 |
| pm.max_spare_servers | 3-5 |
Также включите OPcache и настройте его правильно:
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
6. Код: пишите эффективно
Даже с хорошим сервером плохой код всё испортит. Вот несколько быстрых правил:
- Используйте встроенные функции вместо самописных циклов.
- Избегайте лишних включений файлов (require_once внутри циклов).
- Применяйте генераторы для обработки больших массивов.
- Отключайте ненужные модули PHP.
Пример с генератором:
function readLargeFile($file) {
$handle = fopen($file, 'r');
while (!feof($handle)) {
yield fgets($handle);
}
fclose($handle);
}
foreach (readLargeFile('huge.log') as $line) {
// Обрабатываем строку, не загружая весь файл в память
}
Что в итоге
Оптимизация PHP для высоких нагрузок — это комплекс мер: профилирование, кэширование, грамотная работа с БД, асинхронность, настройка сервера и чистый код. Начните с узких мест, внедряйте постепенно. И помните: лучшая оптимизация — та, которая не ломает функциональность.
Применяйте эти советы, и ваше приложение скажет вам спасибо. Удачи!
Студия WNDER



