Под нагрузкой прилетает
FATAL: too many connections, база отказывается принимать запросы, всё стоитПервая мысль - "надо поднять лимит соединений в базе"
Обычно это лечение симптома, а не причины
А теперь, откуда берётся упор в соединения и почему пул решает это правильно
💰 Соединение к базе - дорогое удовольствие
Каждое подключение к базе - это не бесплатная абстракция
В том же Postgres на каждое соединение заводится отдельный процесс со своей памятью
Их число жёстко ограничено (
max_connections), и не просто так: тысяча соединений - это тысяча процессов, которые сжирают память и заставляют базу тратить силы на переключение между ними, а не на работуПоэтому "просто поднять лимит до 5000" - плохая идея
Ты не уберёшь проблему, а перенесёшь базу из состояния "отказывает" в состояние "еле дышит"
💥 Как упираешься в потолок
Приложение открывает новое соединение на каждый запрос и закрывает после
На малой нагрузке норм
Но под потоком - сто параллельных запросов открывают сто соединений одновременно, тысяча запросов - тысячу Лимит выбивается мгновенно, и новые запросы получают отказ
При этом само соединение ещё и открывается не моментально - на установку уходит время, так что ты платишь дважды
🏊 Пул соединений
Идея простая - не открывать соединение под каждый запрос, а держать наготове небольшой набор уже открытых и переиспользовать их
Запросу нужна база - он берёт свободное соединение из пула, отработал - вернул обратно, не закрывая
Соединений мало, они постоянно в деле, и на установку время не тратится
Пул бывает двух видов:
встроенный в приложение (HikariCP в Java, пулеры в других языках) и внешний - отдельный процесс перед базой (для Postgres это PgBouncer)
Внешний особенно важен, когда инстансов приложения много
⚠️ Ловушка масштабирования
У тебя двадцать инстансов приложения, у каждого свой пул на 20 соединений
Двадцать раз по двадцать — это уже 400 соединений к базе, даже если сейчас тихо
Раскатал автоскейлингом полсотни подов - и снова упёрся в лимит, хотя пулы вроде есть
Вот тут и ставят один общий внешний пулер (PgBouncer) перед базой: приложения ходят в него, а он держит небольшое число реальных соединений к базе
📉 И размер пула - не "чем больше, тем лучше"
Ещё контринтуитивная штука - огромный пул часто медленнее маленького
Если соединений больше, чем база способна реально обрабатывать параллельно (а это упирается в число ядер и диски), они начинают толкаться и мешать друг другу
Небольшой пул, где запросы аккуратно ждут своей очереди, нередко даёт больший throughput, чем раздутый
Так что размер подбирают под возможности базы, а не "на глаз побольше"
too many connections - это почти всегда поставь пул, а под много инстансов — общий внешний пулер, и потолок перестанет быть потолкомА вы на чём ловили «too many connections» — забыли пул, разъехались по инстансам, или крутанули autoscaling? 🤔
#backend #database #postgres #performance #devops #dev