Каждый хоть раз писал такой PHP-скрипт, после запуска которого сервер начинал реветь как турбина, а база данных падала замертво. Обычно это «генерация отчёта за год», «импорт из 500-мегабайтного Excel-а» или «API, который возвращает всё и сразу».

Фокус в том, что чудес не бывает. Если ты гоняешь тяжелый процесс прямо в запросе, пользователю будет больно, серверу будет больно, и тебе тоже. Поэтому надо научиться выносить дичь в фон, кэшировать, дробить или тупо не писать говнокод.

1. Очереди задач: Redis или Beanstalkd через Pheanstalk

Redis (быстро и просто)

Redis часто уже стоит в проекте, и через predis/predis можно легко построить очередь. Это нормальный вариант, если у тебя нет Beanstalkd.

$redis->rpush('task_queue', json_encode(['task' => 'report']));

Beanstalkd (через Pheanstalk)

Если хочешь реально лёгкий сервер для очередей — бери Beanstalkd. Он минималистичный и очень быстрый. В PHP используем Pheanstalk:

composer require pda/pheanstalk

Постановка задачи:

use Pheanstalk\Pheanstalk;

$pheanstalk = Pheanstalk::create('127.0.0.1');

$pheanstalk
    ->useTube('default')
    ->put(json_encode(['task' => 'send_email', 'user_id' => 42]));

Воркер:

$pheanstalk = Pheanstalk::create('127.0.0.1');

while ($job = $pheanstalk->watch('default')->reserve()) {
    $data = json_decode($job->getData(), true);

    echo "Выполняю: {$data['task']} для юзера {$data['user_id']}\n";
    
    // твоя тяжёлая логика
    sleep(3);

    $pheanstalk->delete($job);
}

2. Кэш: не считай дважды

Если у тебя есть операция «полчаса считает, секунду показывает» — кэшируй результат. Файлы, Redis, Memcached — не важно. Важно, что ты не будешь каждый раз долбить базу и молиться.

Простейший файловый кэш:

$cacheFile = 'cache/report.txt';

if (file_exists($cacheFile) && (filemtime($cacheFile) > (time() - 3600))) {
    $data = file_get_contents($cacheFile);
} else {
    $data = performHeavyOperation();
    file_put_contents($cacheFile, $data);
}

Вместо того, чтобы снова и снова гонять «тяжёлый отчёт за месяц», ты один раз посчитал и дальше читаешь готовое.

3. Асинхронка: форкнись или умри

PHP по дефолту однопоточный дегенерат. Но если ты юзаешь pcntl_fork, можно хотя бы распараллелить.

if (function_exists('pcntl_fork')) {
    $pid = pcntl_fork();
    if ($pid == 0) {
        // Дочерний процесс
        performHeavyOperation();
        exit;
    }
}

Или даже тупее: дергай сам себя через curl в фоне (exec('php script.php > /dev/null &')). Это не rocket science, но серверу станет легче.

4. Оптимизация кода: перестань писать дичь

Самое неприятное, но и самое честное. Часто проблема не в железе и не в том, что «PHP медленный», а в том, что ты написал чудовище.

— Сократи количество SQL-запросов.
— Юзай chunk() вместо того, чтобы тащить 10 млн строк в память.
— Не пихай бесконечные циклы с file_get_contents() в прод.

Иногда переписать пару запросов дешевле, чем прикручивать кластер RabbitMQ.

5. Масштабирование: когда всё остальное не спасает

Если ты сделал всё выше, но сервер всё равно стонет — пора доставать кошелёк. Горизонтальное масштабирование, несколько воркеров, облако — да, это больно, но иногда без вариантов.

Итог

Есть пять реальных способов не убить сервер тяжёлым PHP-скриптом:

  1. Очереди.
  2. Кэш.
  3. Асинхронка.
  4. Оптимизация.
  5. Масштабирование.

Если ты думаешь «может, я просто поставлю sleep(1) в цикле, и будет норм?» — нет, не будет.