Таблицы очередей в SQL или отдельный брокер: как выбрать
Сравните сценарии сбоев, усилия команды и пропускную способность, чтобы выбрать самое простое подходящее решение.

Почему это решение быстро усложняется
Большинство команд не начинают с желания внедрять брокер. Им нужна просто рабочая очередь, и они не хотят ещё один сервис, который нужно устанавливать, патчить, мониторить и чинить в два часа ночи. Если приложение уже использует SQL, таблица очереди кажется дешёвым, знакомым и вполне подходящим решением.
Сложности проявляются позже. Задача падает и пытается повториться. Воркер отправляет письмо, а затем падает до того, как пометит строку как выполненную. База данных замедляется, потому что теперь она обслуживает и пользовательский трафик, и фоновые задачи. На диаграмме это не выглядит страшно. Но вы встречаете это во время сбоев, частичных отказов и при наведении порядка.
Вот почему выбор между SQL-таблицей очереди и отдельным брокером редко сводится только к сырой скорости. Речь о том, сколько сбоев ваша команда сможет выдержать до того, как каждый инцидент превратится в ручной ремонт.
Небольшой трафик может вводить в заблуждение. Продукт может обрабатывать несколько сотен задач в обычный день, а потом получить резкий всплеск после биллинга, большого импорта или одного действия клиента, которое разветвляется на тысячи задач. Очередь, которая кажется нормальной в полдень, к 12:15 может серьёзно отстать.
Возьмём простой кейс SaaS. В обычные дни задания по выписке счетов едва заметны. В день выставления счетов система создаёт 80 000 задач за короткое окно. Если один внешний API замедлится и повторные попытки накопятся, бэклог растёт быстро. В этот момент вопрос уже не в абстрактной архитектуре. Вопрос в том, будут ли пользователи ждать, проскальзывают ли дублирующиеся операции и придётся ли кому-то постоянно подглядывать за системой.
Время на операционную работу важно не меньше, чем пропускная способность. Брокер часто лучше справляется со всплесками, но добавляет ещё одну движущуюся часть. Таблица очереди проще на старте, но перекладывает больше ответственности в код приложения, дизайн запросов и настройку базы.
Несколько прямых вопросов обычно выявляют настоящую проблему. Насколько критично дублирование? Как часто задачи будут повторяться во время простоев? Насколько резкие у вас пики? Кто отвечает за систему, когда она начинает неправильно себя вести? Если ответы на эти вопросы туманные, легко выбрать не тот инструмент.
Что меняется, когда очередь оставляют в SQL
Хранение задач в той же базе, что и приложение, обычно упрощает первую версию. Заказы, пользователи, письма, импорты и фоновые задачи находятся в одном месте — команда работает с одной системой вместо двух.
Это также делает записи надёжнее. Если клиент оформляет заказ, приложение может сохранить строку заказа и вставить задачу в одной транзакции. Если транзакция проваливается, ни запись заказа, ни запись задачи не появятся. Не нужно писать дополнительный код, чтобы синхронизировать бизнес-данные и очередь.
Вот почему SQL так хорош на раннем этапе. Вы переиспользуете бэкапы, которым доверяете, те же правила доступа и ту же систему мониторинга. Команде не нужны новые дашборды, новая аутентификация или отдельная ротация звонков только ради фоновых задач.
Отладка тоже часто проще. Когда работа застревает, обычный SQL отвечает на большинство вопросов. Можно проверить, какие задачи выполняются слишком долго, какой тип чаще падает, для какого клиента накапливаются повторы или перестал ли воркер обновлять heartbeat. Разработчик может посмотреть конкретные строки, таймстемпы, payload и счётчики повторов без изучения ещё одного инструмента.
Минус в том, что всё давление ложится на базу данных. Воркеры могут блокировать друг друга при плохом опросе. Таблица очереди может быстро вырасти. Выполненные задачи превращаются в мёртвый груз. Индексы раздуваются, очистка занимает больше времени, а рутинные запросы начинают коснуться больше данных, чем нужно.
Очистка важнее, чем многие команды ожидают. Если вы храните каждую завершённую задачу бесконечно, таблица постепенно превращается в хранилище логов. Если удалять строки крупными взрывами, вы создаёте всплески записи и замедления. Большинству команд нужен ясный план хранения, равномерная чистка и схема таблицы, рассчитанная на постоянные вставки и удаления.
SQL-очередь обычно хорошо подходит, когда объём задач умеренный, обработка ошибок простая, и команда хочет меньше движущихся частей. Она перестаёт казаться дешёвой, когда конкуренция за блокировки, разрастание таблицы или медленный опрос начинают отнимать время каждую неделю.
Что меняется, когда вы добавляете брокер
Брокер переносит поток задач вне основной базы. Это облегчает ситуацию, когда фоновая работа начинает конкурировать с пользовательскими запросами за CPU, диск и пул соединений. Во время всплесков пользовательские записи ведут себя спокойнее, потому что воркеры вытягивают из отдельной системы, а не долбят те же таблицы, которые приложение использует для обычных чтений и записей.
Это особенно важно, когда трафик неравномерный. Всплеск в 50 000 писем, вебхуков или задач по обработке изображений может лежать в брокере, пока приложение продолжает принимать заказы или сохранять формы. С таблицей очереди тот же всплеск часто проявляется в виде медленных запросов, ожиданий блокировок или раздутых индексов в базе, которая уже и так запускает продукт.
Компромисс прост. Вы снимаете нагрузку с SQL, но добавляете ещё один сервис в эксплуатацию. Кому-то теперь нужно установить его, патчить, настраивать, бэкапить и понимать, как выглядит нормальное состояние при отказах.
Брокер также вынуждает команду явно прописать правила доставки. SQL-таблицы позволяют на какое-то время оставаться расплывчатыми, потому что данные «рядом». Брокеры заставляют решить, сколько раз воркер пробует, сколько ждать между попытками, важен ли порядок и когда сообщение отправить в dead letter для ручной проверки.
Эти правила полезны, но требуют работы. Пропустите их, и неудачные задачи могут зациклиться, приходить дважды или исчезнуть в куче, которую никто не проверяет.
Пропускная способность часто улучшается, но только после настройки количества воркеров, подтверждений и батчинга. Брокер не создаёт бесконечную ёмкость. Он даёт вам больше пространства для роста, не опираясь так сильно на базу.
Для небольшой команды это дополнительное пространство стоит своих затрат только тогда, когда очередь сама по себе превращается в отдельную систему. Если объём задач стабилен и невелик, и в базе ещё есть запас, брокер может привнести только лишний шум оповещений. Если команда уже поддерживает несколько сервисов и имеет надёжный мониторинг, дополнительная нагрузка легче принимается.
Где каждая опция ломается первой
Большинство проблем с очередями начинаются со стороны потребителей, а не производителей. Система может весь день принимать работу и всё же сломаться, когда воркеры перестанут успевать.
В случае SQL-очереди первая трещина обычно появляется, когда несколько воркеров сражаются за одни и те же ожидающие строки. Они сканируют одну и ту же таблицу, попадают на одни и те же страницы индексов и ждут блокировок или обновлений строк. Сначала это кажется незначительным: задачи чуть медленнее. Затем база тратит больше времени на поиск работы, чем на её завершение.
Боль распространяется быстро, потому что очередь делит дом с данными приложения. Если опрос очереди становится шумным, обычные чтения и записи тоже замедляются. Дизайн, который хорошо работает с двумя воркерами, может испортиться при двадцати.
Повторы усугубляют ситуацию. Если воркер отправил письмо, списал платёж или записал данные в другую систему, а затем упал до пометки о завершении, следующий воркер может выполнить ту же задачу снова. SQL не создаёт дубликат, но и не защищает от него.
Брокеры дольше скрывают бэклог
Брокер обычно ломается менее очевидно. Продюсеры продолжают публиковать, брокер принимает сообщения, и все думают, что система здорова. Между тем потребители отстают, возраст сообщений растёт, и бэклог превращается в медленную стену.
Поэтому брокеры могут казаться лучше ровно до того момента, когда перестают быть такими. Они поглощают давление, и команды замечают проблему поздно. К тому моменту, когда кто-то проверит lag потребителей, некоторые задачи уже могут устареть и потерять смысл.
Перезапуски и сетевые проблемы тоже меняют время доставки. Потребитель может обработать сообщение, потерять соединение до подтверждения и затем увидеть то же сообщение снова после восстановления. Это нормальное поведение для многих брокеров. Тот же сценарий возможен и при SQL, когда воркер перезапускается между выполнением работы и пометкой о завершении.
Самые вредные — медленные потребители
Медленные продюсеры обычно жить легче, потому что они ограничивают входящую работу. Медленные потребители делают наоборот. Они создают бэклог, устаревшие задачи, шквал повторных попыток и шумные алерты.
Если одна задача занимает 30 секунд, а новые приходят каждую секунду, выбор инструмента проблему не решит. Тогда спор обычно не там: нужны более быстрые обработчики, больше воркеров, более мелкие задачи или строгие лимиты на то, что попадает в очередь.
Именно эта картина важнее всего. SQL часто ломается из-за конкуренции, которую видно в базе. Брокеры часто ломаются из-за лага, который вы не ощущаете, пока он не станет большим. В обоих случаях важнее, чтобы обработчики были безопасны при повторном выполнении, чем идеальные гарантии доставки.
Что команде нужно поддерживать каждый день
Очередь — это не только код. Кто-то должен за ней наблюдать, чистить, настраивать и чинить, когда работа перестаёт двигаться.
С SQL-очередью систем меньше, но база получает больше боли. Если глубина очереди растёт, воркеры выбрасывают больше ошибок или повторов становится больше, чем задач завершается, мелкие проблемы быстро превращаются в тикеты поддержки. Застрявший воркер, плохой релиз или медленный запрос могут оставить задачи в таблице, в то время как приложение снаружи выглядит в порядке.
SQL-очереди также требуют регулярной очистки. Старые задачи, большие payload'ы и длинная история повторов раздувают таблицу, замедляют бэкапы и увеличивают стоимость рутинных запросов. Команда должна настраивать лимиты соединений, конкурентность воркеров, правила повторов и расписание удаления выполненных и «мертвых» задач. По отдельности это несложно, но вместе — набор работ.
Брокер меняет чеклист, но не избавляет от него. Всё равно нужно следить за глубиной, ошибками потребителей, количеством повторов, лагом и dead letter очередями. Также необходима практика рестартов и отработки отказов до реальной поломки. Если нода умирает, потребители переподключаются одновременно или деплой идёт в неправильном порядке, команда должна знать шаги по памяти, а не рыться в полупустой документации.
Здесь команды часто ошибаются в подсчётах. Они сравнивают функции, но не считают время операторской работы. Лучший тест прост: сколько часов в месяц команда тратит на присмотр за очередью? Посчитайте время на очистку застрявших задач, удаление старых payload'ов, настройку конкурентности, проверку дашбордов и обработку ложных тревог. Если это число остаётся низким, обычно выигрывает более простое решение. Если очередь требует постоянного внимания, «дешёвый» дизайн уже не так дешев.
Часто задаваемые вопросы
Стоит ли начинать с таблицы очереди в SQL?
Начинайте с SQL, если объём задач невелик, команда небольшая и приложение уже использует одну базу данных. Это проще в настройке, отладке и позволяет записывать бизнес-дату и задачу в одной транзакции.
Такой выбор перестаёт быть дешевым, когда воркеры начинают бороться за строки, очистка таблицы отнимает время каждую неделю или нагрузка очереди замедляет обычный трафик приложения.
Когда стоит перейти с SQL на брокер?
Брокер имеет смысл, когда пики становятся резкими, один «шумный» тип задач задерживает всё остальное или фоновая работа начинает мешать пользовательскому трафику в основной БД. Он полезен, когда нужна изоляция, больше потребителей или отдельные правила повторных попыток.
Если очередь всё ещё легко просматривать и воркеры редко отстают, брокер может только добавить лишние оповещения и обслуживание.
SQL безопаснее, потому что поддерживает одну транзакцию?
Да. В SQL вы можете записать запись приложения и задачу в одной транзакции. Если сохранение заказа не прошло, вставка задачи тоже не произойдёт.
Это убирает множество вспомогательного кода на раннем этапе, но не решает проблему дублирования после того, как воркер начал выполнение и упал до пометки об окончании.
Что обычно ломается первым при использовании SQL-очереди?
У большинства команд сначала ломается конкуренция за ресурсы. Воркеры опрашивают одну и ту же таблицу, сканируют одни и те же строки и тратят время на получение задач, а не на их выполнение.
Дальше растёт размер таблицы: старые задачи, повторные попытки и большие индексы увеличивают стоимость запросов, замедляют очистку и заставляют базу работать больше и для очереди, и для приложения.
Что обычно ломается первым при использовании отдельного брокера?
У брокера проблема обычно проявляется иначе: продюсеры продолжают публиковать сообщения, сам брокер их принимает, а потребители отстают — лаг растёт. Пока вы отслеживаете только успешную публикацию, беда заметна слишком поздно.
Нужно пристально следить за отставанием потребителей, количеством повторных попыток и трафиком в dead letter очередях.
Как не допустить, чтобы повторы создавали дубликаты работы?
Предполагаете, что каждая задача может выполниться дважды. Воркеры могут отправить письмо, списать деньги или вызвать API, а затем упасть до записи об успехе.
Делайте обработчики идемпотентными, сохраняйте внешние ID запросов, проверяйте текущее состояние перед повторением побочных эффектов и заведите понятные правила повторного воспроизведения для операций с деньгами или сообщениями клиентам.
Стоит ли складывать срочные и массовые задачи в одну очередь?
Нет. Разделяйте срочные задачи и массовые фоновые работы. Сброс пароля или подтверждение оплаты не должны ждать, пока завершатся импорты, аналитика или ресайз картинок.
Разделение очередей позволяет настраивать разные правила повторов и количество воркеров по типу задачи, а не навязывать одно правило всем.
Сколько обслуживания требует SQL-очередь?
Планируйте регулярную очистку с первого дня. Храните выполненные задачи только столько, сколько нужно для поддержки, аудита или отладки, затем удаляйте или архивируйте их малыми, равномерными пакетами.
Если хранить всё навсегда, таблица очереди превратится в хранилище логов — вырастет потребление диска, размеры индексов, скорость бэкапов и стоимость рутинных запросов.
Означают ли редкие пики, что мне нужен брокер?
Не всегда. Одиночный месячный пик сам по себе не обязательно требует брокера, если бэклог быстро очищается и база всё ещё имеет запас ресурсов.
Измерьте реальный пик, возраст самой старой задачи в пике и время восстановления. Если пользователи чувствуют задержку или база замедляется для остальной части приложения, тогда пик имеет значение.
Что стоит протестировать перед заменой текущей очереди?
Запустите «грязные» тесты на текущем решении: сгенерируйте всплеск задач, убейте воркера посередине выполнения, вызовите таймаут и воспроизведите одну и ту же задачу дважды.
Затем измерьте глубину очереди, возраст самой старой задачи, количество повторных попыток и время восстановления. Эти числа скажут больше, чем таблица возможностей.