Решение: как настроить пул соединений?
Каждое соединение с БД несёт затраты памяти и планирования; неконтролируемый рост числа соединений замедляет сервер или даже выводит его из строя. Пул соединений решает это, многократно используя небольшое число постоянных соединений. Это дерево помогает решить, где размещается пул, какой режим использовать (session/transaction/statement) и каков правильный размер, а также подчёркивает, почему внешний пул обязателен в serverless-средах.
Как подключается архитектура вашего приложения?
Решение о пуле начинается с архитектуры. Это один долгоживущий процесс приложения (классический сервер), множество независимых процессов/экземпляров (микросервисы, много подов) или serverless/Lambda-функции, которые создаются и завершаются с каждым запросом? Каждый вариант требует своего размещения пула.
- Один/несколько долгоживущих процессов приложения
- Много процессов/экземпляров/подов подключаются к одной БД
- Serverless / Lambda / короткоживущие функции
- Сначала покажите, как выбрать правильный размер пула
Как выбрать режим промежуточного пула?
При большом числе клиентов промежуточный пул (PgBouncer, ProxySQL) удерживает число соединений с БД на низком уровне. Но режим пула зависит от функций, используемых приложением: полагаетесь ли вы на состояние, привязанное к сессии (подготовленные операторы, временные таблицы, SET, advisory locks, функции уровня сессии), или всё завершается в пределах одной транзакции?
- Я завишу от состояния сессии (SET, временные таблицы, подготовленные операторы сессии)
- Транзакции короткие и независимые; состояние сессии не храню
Рекомендация: пул соединений внутри приложения
Для одного/нескольких долгоживущих процессов большинство потребностей покрывает библиотека пула внутри приложения (HikariCP, встроенный пул database/sql в Go, connection pooling в .NET). Соединения открываются один раз и используются многократно.
Рекомендация: правильно задайте размер пула
Пул не должен быть ни слишком маленьким (запросы ждут), ни слишком большим. Слишком много соединений порождают переключения контекста CPU и конкуренцию за блокировки и снижают пропускную способность. Размер — не число, которое нужно наращивать бесконечно, а небольшое значение, зависящее от ядер и ввода-вывода.
Рекомендация: режим пула session
В режиме session клиентское соединение закрепляется за серверным соединением до окончания сессии. Состояние уровня сессии (SET, временные таблицы, advisory locks, подготовленные операторы сессии) сохраняется, но совместное использование соединений минимально.
Рекомендация: режим пула transaction
В режиме transaction серверное соединение выдаётся клиенту только на время транзакции и возвращается в пул по её завершении. Так тысячи клиентов могут работать с очень небольшим числом реальных соединений с БД; это самый распространённый режим для высокого масштаба.
Рекомендация: для serverless внешний пул обязателен
Serverless/Lambda-функции быстро масштабируются, и каждый экземпляр открывает собственное соединение; пул внутри приложения здесь не помогает, так как экземпляры короткоживущие и ничего не разделяют. Когда внезапно появляются сотни/тысячи экземпляров, лимит соединений БД превышается. Решение — внешний пул соединений перед БД.