Ваш PHP-сайт тормозит? Пора заглянуть под капот
Вы когда-нибудь ждали загрузки страницы дольше, чем пьёте утренний кофе? Знакомая боль. PHP — мощный язык, но без правильной настройки он может превратиться в черепаху. В этой статье мы не будем читать скучные лекции. Мы возьмём реальный код, профилируем его, найдём узкие места и ускорим так, что сервер скажет вам спасибо.
Поговорим о производительности PHP — от простых замеров до глубокого профилирования. Вы узнаете, как с помощью Xdebug и Blackfire находить проблемные участки, и какие методы оптимизации реально работают. Поехали!
Шаг 1. Замеряем всё: от микросекунд до мегабайтов
Прежде чем что-то оптимизировать, нужно понять, где тормозит. Интуиция часто подводит. Давайте использовать инструменты.
Самый простой способ: microtime(true)
Для быстрой проверки куска кода подойдёт старый добрый microtime(). Не элегантно, но работает.
<?php
$start = microtime(true);
// Ваш медленный код
$data = [];
for ($i = 0; $i < 100000; $i++) {
$data[] = md5($i . 'salt');
}
$end = microtime(true);
echo 'Время выполнения: ' . round(($end - $start) * 1000, 2) . ' мс';
?>
Этот метод хорош для грубой оценки. Но когда проект разрастается, нужны серьёзные инструменты.
Xdebug — ваш личный детектив
Xdebug — стандарт де-факто для профилирования PHP. Он генерирует файлы cachegrind, которые можно открыть в KCachegrind (Linux) или QCachegrind (Mac/Windows).
Установка через pecl:
pecl install xdebug
Включим профилирование в php.ini:
xdebug.mode = profile
xdebug.output_dir = /tmp/profiling
После выполнения скрипта в папке появится файл. Откройте его в QCachegrind — вы увидите, какая функция съела больше всего времени. Часто сюрприз: не база данных, а простая обработка строк в цикле.
Blackfire — для тех, кто любит красивые дашборды
Blackfire от SensioLabs (создателей Symfony) — это облачный профайлер. Он показывает не только время, но и количество вызовов, память, I/O. Интегрируется с PHPUnit, Symfony, Laravel.
Пример использования из командной строки:
blackfire run php myscript.php
Вы получите ссылку на веб-отчёт с визуализацией. Удобно искать узкие места производительности PHP.
Шаг 2. Типичные проблемы и их решения
Теперь, когда мы умеем профилировать, давайте разберём частые грабли.
Проблема 1: N+1 запросов в цикле
Классика. Вы получаете список пользователей, а затем в цикле делаете запрос к БД для каждого.
Медленный код:
<?php
$users = $db->query('SELECT id FROM users LIMIT 100');
foreach ($users as $user) {
$orders = $db->query("SELECT * FROM orders WHERE user_id = {$user['id']}");
// ...
}
?>
Оптимизация:
<?php
$users = $db->query('SELECT id FROM users LIMIT 100');
$ids = array_column($users, 'id');
$orders = $db->query('SELECT * FROM orders WHERE user_id IN (' . implode(',', $ids) . ')');
// Группируем по user_id
$grouped = [];
foreach ($orders as $order) {
$grouped[$order['user_id']][] = $order;
}
?>
Результат: 100 запросов превратились в 2. Производительность PHP-приложения вырастает в разы.
Проблема 2: Неэффективные циклы и работа со строками
Конкатенация строк в цикле — зло. Используйте массивы и implode().
<?php
// Плохо
$result = '';
for ($i = 0; $i < 10000; $i++) {
$result .= 'item' . $i . ',';
}
// Хорошо
$parts = [];
for ($i = 0; $i < 10000; $i++) {
$parts[] = 'item' . $i;
}
$result = implode(',', $parts);
?>
Второй вариант работает в 3-5 раз быстрее, потому что не создаёт новые строки на каждой итерации.
Проблема 3: Утечки памяти
PHP-скрипты, особенно долгие (например, воркеры очередей), могут «кушать» память. Используйте memory_get_usage() и unset() для больших переменных.
<?php
function processLargeFile($filename) {
$handle = fopen($filename, 'r');
while (($line = fgets($handle)) !== false) {
processLine($line);
// Очищаем память, если строка большая
unset($line);
}
fclose($handle);
}
?>
Шаг 3. Кэширование — ваш лучший друг
Если данные не меняются каждую секунду — кэшируйте. Оптимизация PHP кода без кэша — это как бежать марафон в ластах.
OPcache — встроенный ускоритель
PHP 7+ включает OPcache. Он хранит скомпилированный байт-код в памяти, избавляя от компиляции при каждом запросе.
Проверьте настройки в php.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
Это даёт прирост производительности на 30-50% без единой строчки кода.
Redis/Memcached для данных
Если вы часто делаете одни и те же запросы к БД, сохраняйте результат в Redis.
<?php
$cacheKey = 'popular_articles';
$articles = $redis->get($cacheKey);
if (!$articles) {
$articles = $db->query('SELECT * FROM articles WHERE views > 1000 ORDER BY created_at DESC LIMIT 10');
$redis->setex($cacheKey, 3600, serialize($articles));
} else {
$articles = unserialize($articles);
}
?>
Время загрузки страницы падает с 200 мс до 5 мс