Прихована ціна простого рішення.
Майже кожен Laravel-розробник на старті робить один і той самий вибір.
Потрібно надіслати welcome email, обробити експорт, змінити розмір зображення або виконати фонове завдання. Ви відкриваєте .env, ставите QUEUE_CONNECTION=database, запускаєте міграцію і йдете далі. Це просто, вбудовано в Laravel і чудово працює на локальній машині.
Саме в цій простоті і ховається проблема.
На малому навантаженні database queue здається нешкідливою. На реальному масштабі ви перетворюєте головну базу даних на транспорт для тимчасових фонових задач. Замість того щоб займатися своїм основним завданням, тобто зберігати та віддавати бізнес-дані, база починає витрачати ресурси на нескінченний цикл: запис, блокування, опитування, оновлення, видалення.
І це зовсім не те, для чого вона створювалася.
Бази даних створені для зберігання, а не для queue churn
Реляційні бази даних на кшталт MySQL або PostgreSQL чудово підходять для надійного зберігання, індексації, транзакцій, зв'язків між даними та консистентного читання. Але вони не є найкращим інструментом для постійного потоку короткоживучих фонових завдань.
У черги дуже специфічний патерн роботи:
- записати задачу
- зарезервувати її
- заблокувати
- обробити
- видалити або позначити як failed
Коли Laravel використовує database driver для черг, воркери постійно конкурують за одну й ту саму таблицю. Щоб два процеси не взяли одне й те саме завдання, система покладається на механізми блокування рядків, наприклад SELECT ... FOR UPDATE або подібні підходи резервування.
Саме тут і починається справжня проблема.
Коли трафік зростає, queue workers створюють конкуренцію навколо спільної таблиці. І ця конкуренція не залишається ізольованою. Вона починає впливати на весь застосунок, тому що той самий сервер бази даних одночасно обслуговує користувацькі запити, авторизацію, дашборди, фільтри, звіти, адмінку та інші критичні частини системи.
У якийсь момент застосунок починає гальмувати не через користувачів, а через те, що фонові процеси борються між собою.
Вузьке місце, яке не видно одразу.
Найнебезпечніше в цій архітектурній помилці те, що вона проявляється поступово.
Спочатку все ніби працює. Задачі обробляються. Сторінки відкриваються. Нічого не виглядає критично зламаним.
Потім з'являються перші симптоми:
- фонові задачі починають накопичуватися
- воркери дедалі більше часу витрачають на опитування таблиці
- ростуть lock wait
- частішають failed jobs
- збільшується навантаження на CPU і диск
- запити додатка стають менш передбачуваними
Зрештою таблиця черги стає гарячою точкою системи. Вона вже не є нейтральним транспортом для задач. Вона стає спільним bottleneck між вашим продуктом і його інфраструктурою.
Це одна з тих проблем, які довго маскуються під зручність.
Чому Redis змінює правила гри.
Redis значно краще підходить для ролі черги, тому що він спроєктований для дуже швидких операцій у пам'яті.
Це не просто кеш. Це сховище структур даних, оптимізоване для надшвидких читань і записів із дуже низькою затримкою та набагато меншим рівнем конкуренції, ніж у реляційної таблиці черги.
І це критично важливо, тому що черга не повинна бути довготривалим сховищем. Її задача полягає у швидкому та надійному переміщенні роботи між частинами системи.
Redis підходить для цього значно краще, бо:
- операції виконуються в оперативній пам'яті
- затримка значно нижча
- резервування задач працює ефективніше
- воркери не б'ються за одну SQL-таблицю
- основна база даних залишається зосередженою на бізнес-даних
На практиці це означає більш стабільну роботу під навантаженням, швидшу обробку задач і передбачуваніше масштабування.
Головне архітектурне правило.
База даних має зберігати стан.
Черга має переносити роботу.
Це різні ролі, і якщо занадто довго змішувати їх в одному місці, наслідки рано чи пізно стають болючими.
Database queue підходить для локальної розробки, прототипів, маленьких внутрішніх інструментів або раннього MVP з мінімальною конкуренцією. Але щойно у вас з'являються реальні email-флоу, імпорти, webhook-и, нотифікації, генерація файлів, аналітика або розкладені задачі, черга заслуговує на інфраструктуру, створену саме для черг.
Laravel Horizon - це не опція, якщо вам важлива видимість.
Після переходу на Redis Laravel Horizon стає одним із найкорисніших інструментів у стеку.
Він дає нормальний операційний контроль над чергами:
- throughput
- час очікування
- активність воркерів
- failed jobs
- повторні спроби
- балансування навантаження між чергами
Без такої видимості черга швидко перетворюється на чорну скриньку. Задачі можуть падати тихо, retry накопичуються, а проблема стає помітною лише тоді, коли вже скаржаться користувачі.
З Horizon ви перестаєте вгадувати.
Коли database queue все ще допустима.
Щоб бути чесними, database driver не є чимось поганим сам по собі. Він просто має межі.
Він цілком доречний, якщо:
- ви працюєте локально
- трафік ще дуже малий
- задачі рідкісні
- вимоги до продуктивності невисокі
- ви хочете мінімум інфраструктури на старті
Але його варто сприймати як тимчасовий етап, а не як фінальне рішення.
Помилка не в тому, що ви почали з database queue. Помилка в тому, що ви залишилися там надто надовго.
Висновок.
Database queue здається дешевим рішенням, бо дозволяє відкласти інфраструктурні рішення на потім.
Насправді вона часто просто переносить проблему в майбутнє, до моменту, коли вона вже починає боліти.
Якщо ваш Laravel-додаток виконує реальну фонову роботу, винесіть черги в Redis до того, як bottleneck почне шкодити всій системі. База даних працюватиме спокійніше, воркери рухатимуться швидше, а ви витратите менше часу на дебаг проблем, яких можна було уникнути.
М'який CTA.
Хочете витрачати менше часу на повторне збирання типової продуктової інфраструктури?
Створіть професійну сторінку з бронюванням, календарем і зручнішим клієнтським сценарієм разом із Meetfolio.