Очередь на базе в Laravel: что нужно знать до старта
Как поднять очередь заданий на драйвере database в Laravel 11: миграции, воркер, обработка сбоев. Пошагово от нуля до работающей задачи в фоне за 20–30 минут.

К концу этой инструкции у вас будет очередь на драйвере database, отдельный воркер, который забирает задания из таблицы, и одно тестовое задание, уходящее в фон. Отправка письма, генерация PDF или вызов внешнего API перестанут держать HTTP-запрос — пользователь получит ответ сразу, а тяжёлая работа выполнится позже. На чистом проекте это занимает 20–30 минут. Всё написано под Laravel 11.x; для 10.x отличия отмечены отдельно.
Драйвер database хранит задания в обычной таблице MySQL или PostgreSQL. Это самый простой способ начать: не нужно поднимать Redis или разбираться с внешними сервисами. Он медленнее Redis и хуже держит высокую параллельность, но для магазина, блога или админки со скромным потоком заданий его хватает с запасом.
Что нужно иметь до начала
Проверьте это до первой команды, чтобы не упереться на середине.
- PHP 8.2+ и Laravel 11.x (проверьте
php artisan --version). - Настроенная и работающая база: MySQL 8+ или PostgreSQL 14+. Драйвер очереди пишет в ту же БД, что и приложение, если вы не указали отдельное соединение.
- Доступ к консоли на сервере, где крутится приложение. Воркер — это долгоживущий процесс, его нельзя запустить «из браузера».
- Право установить процесс-менеджер (Supervisor на Linux) или доступ к панели хостинга, где есть управление демонами. Без этого воркер придётся перезапускать руками.
Если приложение на shared-хостинге без SSH и без возможности держать фоновый процесс — database-очередь работать не будет так, как задумано. Это ограничение хостинга, а не Laravel.
Шаг 1. Указать драйвер database
Откройте .env и задайте соединение очереди:
QUEUE_CONNECTION=databaseПо умолчанию в свежем Laravel 11 стоит database, но проверьте — в старых проектах там может быть sync, при котором задания выполняются немедленно и никакой очереди на деле нет.
Проверка: выполните php artisan config:show queue.default — должно вывестись database.
Шаг 2. Создать таблицы очереди
Laravel 11 хранит определения таблиц очереди и заданий в отдельной команде. Сначала опубликуйте миграции:
php artisan make:queue-table
php artisan make:queue-failed-table
php artisan migrateПервая команда создаёт миграцию для таблицы jobs — там лежат задания, ожидающие обработки. Вторая — для failed_jobs, куда попадают задания, упавшие после всех попыток. В Laravel 10.x команда называется php artisan queue:table, а таблица сбоев публикуется через php artisan queue:failed-table — проверьте по своей версии.
Проверка: загляните в БД. Должны появиться таблицы jobs и failed_jobs. Таблица jobs пока пустая — это нормально.
Шаг 3. Создать класс задания
Задание — это класс, который умеет выполнять одну единицу работы. Создаём:
php artisan make:job SendWelcomeEmailПоявится файл app/Jobs/SendWelcomeEmail.php. Наполните его так:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// Сколько раз пробовать задание перед отправкой в failed_jobs
public int $tries = 3;
// Через сколько секунд считать задание зависшим и вернуть в очередь
public int $timeout = 60;
public function __construct(public int $userId)
{
}
public function handle(): void
{
// Здесь реальная работа: отправка письма, вызов API и т.п.
Log::info('Отправляю приветствие пользователю', ['user' => $this->userId]);
}
}Важный момент: интерфейс ShouldQueue — это то, что отправляет задание в очередь. Без него оно выполнится синхронно. В конструктор передавайте только простые данные (id, строки), а не целые модели с состоянием — свойство SerializesModels само подгрузит модель из БД по id при выполнении.
Шаг 4. Отправить задание в очередь
Из контроллера, команды или сервиса:
use App\Jobs\SendWelcomeEmail;
SendWelcomeEmail::dispatch($user->id);Проверка: после вызова загляните в таблицу jobs — там появится строка. Если строки нет, а работа выполнилась сразу — у вас всё ещё драйвер sync, вернитесь к шагу 1.
Шаг 5. Запустить воркер
Пока задание лежит в таблице, но никто его не забирает. Нужен воркер:
php artisan queue:work --queue=default --tries=3Проверка: в консоли появится строка вида Processing: App\Jobs\SendWelcomeEmail, а следом Processed. Строка из таблицы jobs исчезнет. В storage/logs/laravel.log будет запись из Log::info.
Главная ловушка новичка:queue:workзагружает код один раз при старте. Изменили класс задания — воркер продолжает крутить старую версию, пока вы его не перезапустите. На локальной разработке для этого естьqueue:listen, который перечитывает код каждый раз, но он медленнее и в проде не используется.
Шаг 6. Держать воркер живым через Supervisor
Ручной queue:work умрёт, как только вы закроете терминал. В проде процессом управляет Supervisor. Пример конфига /etc/supervisor/conf.d/laravel-worker.conf:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600Флаг --max-time=3600 перезапускает воркер раз в час, чтобы утечки памяти не копились. stopwaitsecs даёт заданию доработать при остановке. После правки конфига:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*Деплой: не забыть про рестарт
Раз воркер держит старый код в памяти, после каждого деплоя его нужно перезапускать. Добавьте в скрипт выката команду:
php artisan queue:restartОна посылает всем воркерам сигнал завершиться после текущего задания, а Supervisor поднимет их заново — уже с новым кодом. Без этого шага задания будут выполняться по старой логике до случайного перезапуска.
Если не получилось
Table 'jobs' doesn't exist— не выполнена миграция из шага 2. Запуститеphp artisan migrateи проверьте, что миграция очереди действительно применилась.- Задание выполняется мгновенно, таблица
jobsпустая — драйверsync. ПроверьтеQUEUE_CONNECTIONв.envи сбросьте кеш конфига:php artisan config:clear. - Строка в
jobsпоявилась и не уходит — воркер не запущен либо слушает не ту очередь. Убедитесь, что--queueу воркера совпадает с очередью, куда уходит задание (по умолчаниюdefault). - Задание переехало в
failed_jobs— внутриhandle()вылетело исключение$triesраз. Посмотрите причину:php artisan queue:failedпокажет список, а логи — текст ошибки. После починки повторите:php artisan queue:retry all. - После правки кода ничего не меняется — воркер крутит старую версию. Выполните
php artisan queue:restartлибо перезапустите процесс в Supervisor.
Что настроить дальше
Когда базовая очередь работает, следующие шаги по важности: обработка упавших заданий (метод failed() в классе задания и мониторинг таблицы failed_jobs), разнесение заданий по приоритетам через отдельные очереди (--queue=high,default), настройка backoff для задержки между повторами. Когда упрётесь в производительность database-драйвера — а это первое, что ломается под потоком, — смотрите в сторону Redis: смена драйвера почти не затрагивает код заданий. Инвалидация зависших заданий и горизонтальное масштабирование воркеров — отдельные темы, здесь их не разбираем.
AI-стилист и фотограф: Замена фона, ретушь, аватарки, фото для резюме.
Спросить за 20 ₽Источники: Laravel Queues — официальная документация, Deploying with Supervisor — Laravel docs
Частые вопросы
У меня Laravel 10, команды из статьи не работают. Что делать?
Задание попало в failed_jobs — данные потеряны?
Можно ли обойтись без Supervisor?
Почему после деплоя задания выполняются по старой логике?
Драйвер database выдержит нагрузку интернет-магазина?
Нужно ли передавать в задание всю модель или лучше id?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.