280 000 запросов к базе на один пост: как мы нашли и убили тормоз в рассыльщике

Пять дней система рекламы наших вилл молчала. Боты не отправляли ни одного сообщения в Telegram-группы, хотя должны были слать посты каждые два-три часа. Мониторинг показывал: сервер горит, 27% процессора на одной задаче. Я начал разбираться — и нашёл одну из самых нелепых ошибок за всё время работы с автоматизацией.

Симптомы: 5 дней тишины

У нас есть система рассылки объявлений о виллах в Telegram-группы по аренде недвижимости на Бали. Работает она через 9 Telegram-аккаунтов, каждый со своим прокси. Логика проста: система берёт виллу из очереди, формирует пост с фото и описанием, и отправляет в группу. Ротация вилл, ротация групп, интервалы между отправками — всё настроено.

Но в один момент всё остановилось. Посты перестали уходить. Сервер при этом не упал — он просто задыхался. CPU на одном процессе висел на 27%, хотя обычно рассыльщик потребляет 1-2%.

Диагностика: 280 000 запросов на одно действие

Когда начал копать, обнаружил причину в функции выбора следующей виллы для публикации. Эта функция должна была определить: какую виллу давно не рекламировали, в какую группу её лучше отправить, не было ли дублей за последние часы.

Проблема была архитектурная. Функция для каждой виллы (16 штук) делала отдельный запрос в базу по каждой группе (20+ групп), проверяя историю отправок. Потом для каждой пары «вилла — группа» ещё подтягивала статистику просмотров, реакций и блокировок. Итого: 16 вилл × 20 групп × ~900 записей истории = почти 280 000 отдельных SQL-запросов на один цикл выбора поста.

Это как зайти на рынок и для каждого прилавка отдельно спрашивать продавца: «Что у вас есть?» — вместо того чтобы один раз пройти по рядам и составить список.

Решение: с 280 000 до 10 запросов

Переписали логику полностью. Вместо поэлементных запросов сделали три агрегирующих SQL-запроса:

  • Запрос 1: Получить все виллы с датой последней отправки (один JOIN с таблицей broadcast_log, сортировка по давности)
  • Запрос 2: Получить статистику по группам — где вилла уже была за последние 24 часа (GROUP BY с HAVING)
  • Запрос 3: Проверить блокировки и ограничения аккаунтов (простой SELECT с фильтром по статусу)

Плюс добавили материализованный кэш ротации: раз в час обновляется таблица с приоритетами вилл, и рассыльщик просто берёт верхнюю строку. Итого вместо 280 000 запросов — 10. Не десять тысяч. Десять.

Результат: CPU с 27% до 1.6%

После деплоя изменений нагрузка на процессор упала с 27% до 1.6%. Рассыльщик снова начал работать в штатном режиме. Время выбора следующего поста сократилось с 45 секунд до 200 миллисекунд.

Но самое неприятное — те 5 дней простоя. За это время система не отправила ни одного поста в группы, где суммарно более 50 000 участников. Грубая оценка: потеряно около 300-400 потенциальных просмотров объявлений в сутки, то есть 1500-2000 за пять дней.

Урок: мониторинг должен ловить не только падения

Сервер не падал. Сервис не крашился. Он просто работал бесконечно медленно — настолько, что не успевал отправить ни одного поста до следующего цикла. Классический silent failure: технически всё работает, результата — ноль.

После этого инцидента я добавил в health-check проверку не только статуса процесса, но и метрик результативности: если за последние 4 часа не было ни одной успешной отправки — это алерт. Неважно, что процесс running и CPU не 100%. Если нет output — значит, что-то сломано.

Ключевые цифры:
SQL-запросов: 280 000 → 10
CPU: 27% → 1.6%
Время выбора поста: 45 сек → 200 мс
Простой: 5 дней
Причина: поэлементные запросы вместо агрегации

Выводы

Эта история — хороший пример того, как проблема с данными проявляется не сразу. Пока вилл было 8 и групп 10, система справлялась. Но когда вилл стало 16, а групп больше 20, квадратичная сложность запросов убила производительность. При масштабировании линейный рост данных может вызвать экспоненциальный рост нагрузки, если архитектура запросов не оптимизирована.

Второй вывод: мониторинг по метрикам процесса (CPU, memory, uptime) недостаточен. Нужен мониторинг по бизнес-метрикам — сколько постов отправлено, сколько лидов получено, какой отклик. Если бизнес-метрика упала в ноль — это критический алерт, даже если все технические показатели в норме.

Читайте также

Подписаться на блог в Telegram

Читайте свежие кейсы об AI-автоматизации, системной архитектуре и масштабировании бизнеса.

Подписаться