Queue-SQL: масові видалення та оновлення даних через паралельні Queue Jobs

Перекладено ШІ 0 Laravel News 30 липня, 2026

Масштабні операції з базою даних більше не паралізуватимуть систему завдяки новому пакету Queue-SQL. Дізнайтеся, як ефективно розбивати важкі запити на паралельні Batch-завдання для безпечного оновлення мільйонів рядків у Laravel.

Виконання одного запиту UPDATE або DELETE для мільйонів рядків може заблокувати базу даних на кілька хвилин, спричинити відставання реплік або призвести до тайм-аутів під час деплою.

Queue-SQL — це пакет для Laravel від Камрана Атаєва, який розділяє масові операції запису на паралельні черги завдань. Пакет обчислює мінімальні та максимальні значення primary keys, розбиває цей діапазон на сегменти та запускає Illuminate\Bus\Batch для незалежної обробки кожної частини.

Основні можливості пакета:

  • Макрос для Query builder: додає метод queue() до екземплярів Query Builder та Eloquent, відкриваючи доступ до операцій update, delete, insert та upsert.
  • Безпечна компіляція SQL: перетворює обмеження на SQL-фрагменти та bindings. Це дозволяє передавати воркерам вкладені замикання, whereHas, whereExists та sub-selects без помилок closure serialization.
  • Гнучке налаштування розміру пачок: регулюйте обсяг завдань через chunk (кількість рядків на завдання) або maxJobs (бажана загальна кількість завдань).
  • Нативні Laravel Batches: повертає екземпляр Illuminate\Bus\Batch із підтримкою методів then, catch, finally та allowFailures.
  • Моніторинг через Artisan CLI: відстежуйте та керуйте активними процесами за допомогою queue-sql:status та queue-sql:cancel. Доступний режим реального часу --watch та вивід у --json.
  • Тегування в Horizon: автоматично додає до завдань тег queue-sql, назву операції та цільову таблицю.
  • Глобальні налаштування: у файлі config/queue-sql.php можна задати дефолтні значення для chunk size, retries, backoff, throttling та назви черг.

# Запуск запитів у чергу

Щоб відправити запит у чергу, додайте queue() перед методом запису, вкажіть параметри та викличте dispatch() наприкінці. Завдання не потраплять у чергу до моменту виклику dispatch():

use App\Models\Order;
 
Order::where('status', 'complete')
    ->queue(chunk: 25000, tries: 2, onQueue: 'maintenance', throttle: 4)
    ->update(['status' => 'completed'])
    ->then(fn (Batch $batch) => Log::info("Оновлення завершено за {$batch->totalJobs} кроків"))
    ->catch(fn (Throwable $e) => report($e))
    ->dispatch();

Параметр throttle незамінний для великих оновлень: обмеження швидкості (наприклад, до чотирьох завдань на секунду) запобігає перевантаженню з’єднань із базою даних та лагам реплікації.

Видалення працює за аналогічним синтаксисом. Масові вставки (inserts) натомість просто ділять масив записів між завданнями, оминаючи етап планування діапазонів ключів:

PersonalAccessToken::where('expires_at', '<', now()->subMonths(6))
    ->queue(chunk: 10000)
    ->delete()
    ->dispatch();
 
DB::table('search_index')->queue(chunk: 1000)->insert($rows)->dispatch();

Оскільки кожне завдання update або delete працює з фіксованим зрізом primary keys, повторний запуск є ідемпотентним. Проте масові вставки цієї властивості не мають: повтор завдання insert може створити дублікати. Для безпечних повторів документація рекомендує використовувати унікальні індекси або upsert().

На відміну від стандартного трейту Prunable та команди model:prune, які видаляють записи послідовно в одному процесі, Queue-SQL виконує завдання паралельно. Крім того, пакет підтримує довільні updates та масові inserts, чого не вміє Prunable.

# Попередній перегляд плану

Метод dryRun() замість dispatch() покаже статистику майбутньої операції без фактичного запуску завдань:

Order::where('status', 'complete')
    ->queue(chunk: 25000)
    ->update(['status' => 'completed'])
    ->dryRun();
 
// ['operation' => 'update', 'table' => 'orders', 'jobs' => 412, 'ranges' => 412, 'estimatedRows' => 9842311]

Якщо ви хочете обмежити загальну кількість завдань замість розміру пачки, використовуйте maxJobs:

Order::where('status', 'complete')
    ->queue(maxJobs: 100)
    ->update(['status' => 'completed'])
    ->dispatch();

Розподіл за діапазоном базується на мінімальному та максимальному значеннях primary key, а не на точній кількості рядків. Якщо в таблиці є великі пропуски в ID, деякі завдання можуть обробляти менше рядків, ніж інші, але загальна кількість завдань залишиться фіксованою. Зауважте: chunk та maxJobs не можна використовувати одночасно — це викличе InvalidArgumentException.

# Моніторинг черги через CLI

Виклик dispatch() повертає об'єкт Illuminate\Bus\Batch. Ви можете зберегти $batch->id для подальшого відстеження або переглянути активні процеси через Artisan:

php artisan queue-sql:status            # список усіх пакетів
php artisan queue-sql:status {batch}    # деталі конкретного пакета
php artisan queue-sql:cancel {batch}    # скасування
php artisan queue-sql:status --watch    # оновлення кожні 2 секунди
php artisan queue-sql:json              # вивід у JSON

Інтерфейс відображає кількість завдань, статус виконання, прогрес у відсотках та кількість помилок. Прапор --watch працює в інтерактивних терміналах, тоді як у скриптах автоматизації вивід друкується одноразово.

# Встановлення та вимоги

Queue-SQL вимагає PHP 8.1+ та Laravel версій від 10 до 13. Підтримуються SQLite, MySQL та PostgreSQL. Оскільки пакет базується на нативних job batches, переконайтеся, що у вас створена відповідна таблиця:

composer require kamranata/queue-sql
php artisan queue:batches-table
php artisan migrate
php artisan vendor:publish --tag=queue-sql-config   # опціонально

Важливі нюанси роботи:

  1. Вимога до Primary Key: для сегментації запитів потрібен числовий primary key (auto-incrementing integer). Якщо його немає, запит виконається одним завданням.
  2. Фіксовані межі ключів: межі обчислюються в момент виклику dispatch(). Рядки, додані з вищими ID вже після запуску, не потраплять у поточний пакет.

Репозиторій містить скрипт benchmarks/lock_duration.php для тестів продуктивності. Тестування видалення 20 000 рядків у SQLite показало, що звичайний DELETE утримує блокування бази в 3.6 раза довше, ніж сегментована операція через Queue-SQL.

Більше інформації, повну документацію та вихідний код можна знайти на GitHub-сторінці queue-sql.

Популярні

Інше, що варто прочитати

19 Оновлено 26 червня, 2026

Intervention Image: потужний інструмент для роботи з зображеннями у Laravel

Досліджуйте потужний пакет Intervention Image для PHP, який виводить редагування зображень на новий рівень з оновленою версією 3. Чи готові ви дізнатися, які нові можливості та функції чекають на вас у цьому інструменті

59 Оновлено 26 червня, 2026

Усе, що нам відомо про Livewire 4

Нова версія Livewire 4, представленої Келебом Порзіо на Laracon US 2025, обіцяє значні покращення у швидкості та організації компонентів. Які з інноваційних функцій підкорять ваше серце? Читайте далі, щоб дізнатися більше про те, як Livewire 4 полегшить вашу роботу

15 Оновлено 26 червня, 2026

Як задокументувати кілька API в Laravel за допомогою Scramble

Ви знали, що в одному додатку Laravel можна реалізувати кілька API? У нашій статті ви дізнаєтеся, як за допомогою Scramble легко документувати різні версії API та налаштувати доступ до документації, щоб зробити її публічною або приватною. Читайте далі, щоб дізнатися більше