Как решать проблемы с производительностью приложения при нагрузке с использованием Postgres, RabbitMQ и объектного хранилища?
Что проверяют
Есть ли у вас порядок действий: сначала найти узкое место измерениями, потом чинить именно его. Следом обычно спрашивают, что вы сделаете, если очередь в RabbitMQ растёт быстрее, чем её разбирают.
Как отвечать
Первый шаг это метрики и профилирование, а не догадки: задержки p95 и p99, запросы в секунду, доля ошибок, трассировка через OpenTelemetry, py-spy на живом процессе. Дальше по частям:
- Postgres: медленные запросы ищут через
pg_stat_statementsи разбираютEXPLAIN (ANALYZE, BUFFERS). Частые виновники: N+1 из ORM (в SQLAlchemy лечитсяselectinload), нет нужного индекса, долгие транзакции держат блокировки. Каждое соединение Postgres это отдельный процесс, поэтому сотня воркеров со своими подключениями сама по себе нагрузка: нужен пул в приложении и PgBouncer перед базой. Массовую запись делают пачками или черезCOPY; - RabbitMQ: смотрят глубину очереди и число неподтверждённых сообщений. Без
prefetch_countброкер отдаёт консьюмеру сколько угодно сообщений, один воркер набирает тысячи, остальные простаивают.ackшлют после обработки, консьюмеров масштабируют горизонтально, битые сообщения уводят в dead letter exchange, а обработчик делают идемпотентным: доставка идёт «как минимум один раз»; - объектное хранилище: файлы не гоняют через приложение, клиент грузит и скачивает их по presigned URL, большие объекты идут multipart-загрузкой и потоком, а не целиком в память. В очередь кладут ссылку на объект, а не сам файл.
Отдельно про Python: boto3 и синхронные драйверы базы блокируют event loop, если вызывать их из асинхронного кода. Их выносят в пул потоков или меняют на асинхронные. CPU-тяжёлую работу отдают процессам: в обычной сборке CPython GIL не даёт потокам ускорить вычисления.
Где спотыкаются
Сразу предлагают «добавить Redis». Кэш помогает, когда узкое место в чтении одинаковых данных, и ничего не даёт при медленной записи или забитой очереди.
Масштабируют число воркеров, не посчитав соединения к базе. Итог: упор в max_connections вместо ускорения.
Начинают с настройки обменников, когда в очереди копятся сообщения. Сначала надо понять, сколько стоит одна обработка и где она ждёт.