Skip to content
← Все статьи

Пул соединений с БД: как работает и лучшие практики

Зачем нужен пул соединений

Каждый запрос к базе данных требует соединения. Создание нового соединения включает TCP-handshake, аутентификацию, выделение памяти на сервере БД и инициализацию сессии. Для MySQL это занимает 5–50 мс. Для PostgreSQL — 50–100 мс из-за форка процесса. Умножьте это на тысячи запросов в секунду, и накладные расходы на соединения становятся серьёзным узким местом.

Пул соединений решает эту проблему, поддерживая набор заранее установленных соединений, которые разделяются между запросами. Вместо создания и уничтожения соединений приложение берёт соединение из пула, использует его и возвращает обратно. Результат: значительно меньшая задержка, более высокая пропускная способность и сниженная нагрузка на сервер БД.

Как работает пул соединений

Пул соединений — это кэш подключений к базе данных. Жизненный цикл:

  • Инициализация — пул создаёт минимальное количество соединений при запуске (minimum pool size)
  • Заимствование — когда коду нужно соединение, он запрашивает его у пула. Если есть свободное — возвращается немедленно. Если нет — создаётся новое (до максимального размера пула)
  • Использование — приложение выполняет запросы через заимствованное соединение
  • Возврат — после использования соединение возвращается в пул
  • Вытеснение — простаивающие соединения, превысившие idle timeout, закрываются для освобождения ресурсов
  • Валидация — перед выдачей пул может проверить, что соединение живо (Ping или тестовый запрос)

Настройка размера пула

Самая критичная настройка. Слишком мал — запросы встают в очередь за соединениями, увеличивая задержку. Слишком велик — база данных перегружена параллельными соединениями, каждое из которых потребляет память и CPU.

ПараметрОписаниеТипичное значение
min_pool_sizeСоединения, создаваемые при старте5–10
max_pool_sizeМаксимальное число одновременных соединений20–50
idle_timeoutЗакрытие простаивающих соединений после300 с
connection_timeoutМаксимальное ожидание соединения из пула5–30 с
max_lifetimeЗакрытие соединения после общего времени жизни1800 с

Формула оптимального размера пула

Широко цитируемая формула из вики PostgreSQL:

pool_size = (количество_ядер * 2) + количество_дисков

Пример для 4-ядерного сервера с SSD:
pool_size = (4 * 2) + 1 = 9

Это отправная точка, а не окончательный ответ. Правильный размер зависит от длительности запросов, паттернов конкурентности и типа нагрузки (CPU-bound или I/O-bound). Профилируйте под реалистичной нагрузкой.

Распространённая ошибка — слишком большой пул. Производительность PostgreSQL значительно деградирует при более чем ~100 соединениях из-за конкуренции за блокировки и переключения контекста. MySQL выдерживает больше, но тоже страдает при 200–300+.

Пул соединений в PHP

Жизненный цикл PHP-запроса создаёт уникальную проблему: каждый запрос обычно создаёт новое соединение с БД и уничтожает его в конце. Нативный PHP не имеет встроенного пулирования. Решения:

  • Persistent connectionsPDO::ATTR_PERSISTENT = true повторно использует соединения между запросами одного worker-процесса. Просто, но ограничено: нет контроля размера пула, возможны утечки
  • ProxySQL — middleware-прокси, пулирующий соединения между PHP и MySQL. Гибко настраивается, поддерживает мультиплексирование
  • MySQL Router — официальный middleware от MySQL для управления соединениями и балансировки
  • Swoole/RoadRunner — PHP application серверы с поддержкой пулов соединений между запросами
// PHP PDO persistent connection
$pdo = new PDO(
    'mysql:host=localhost;dbname=myapp',
    'user',
    'password',
    [PDO::ATTR_PERSISTENT => true]
);

// С ProxySQL — подключение через прокси вместо MySQL напрямую
$pdo = new PDO(
    'mysql:host=127.0.0.1;port=6033;dbname=myapp',
    'user',
    'password'
);

Пул соединений с PgBouncer

PgBouncer — самый популярный пулер для PostgreSQL. Он располагается между приложением и PostgreSQL, управляя пулом серверных соединений:

# /etc/pgbouncer/pgbouncer.ini
[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_port = 6432
listen_addr = 127.0.0.1
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3

PgBouncer поддерживает три режима пулирования:

  • Session mode — одно серверное соединение на клиентскую сессию. Самый безопасный, но наименее эффективный
  • Transaction mode — соединение назначается на транзакцию. Лучший для большинства веб-приложений. В 10 раз эффективнее использование соединений
  • Statement mode — соединение назначается на оператор. Самый агрессивный, но ломает многооператорные транзакции

Мониторинг пулов соединений

Метрики для отслеживания:

  • Активные соединения — используемые в данный момент. Если постоянно равны max_pool_size — пул насыщен
  • Простаивающие соединения — ожидающие работы. Слишком много — трата памяти БД
  • Длина очереди ожидания — запросы, ждущие соединения. Ненулевое значение = пул слишком мал или запросы слишком медленные
  • Частота создания соединений — частое создание = отток, что нивелирует смысл пулирования
  • Среднее время ожидания — время ожидания соединения. Должно быть близко к нулю
-- MySQL: проверка активных соединений
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
SHOW STATUS LIKE 'Max_used_connections';

-- PostgreSQL: проверка соединений
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;

Типичные ошибки

  • Утечки соединений — заимствование без возврата. В итоге пул исчерпывается, и все запросы зависают. Всегда используйте try/finally
  • Пул на каждый экземпляр микросервиса — 20 экземпляров с пулом 50 = 1000 соединений к БД. Координируйте размеры пулов между сервисами
  • Отсутствие валидации — протухшие соединения (после перезапуска БД, сетевого сбоя) вызывают ошибки. Включите проверку test-запросом
  • Длинные транзакции — транзакция, удерживающая соединение секунды, блокирует другие запросы, фактически уменьшая размер пула

Лучшие практики

  • Начинайте с небольшого пула (10–20) и увеличивайте на основе мониторинг сайтов
  • Устанавливайте connection timeout для предотвращения бесконечного ожидания (5–10 секунд)
  • Используйте transaction-mode для веб-нагрузок (PgBouncer) или ProxySQL для MySQL
  • Активно мониторьте насыщение пула и метрики очереди ожидания
  • Убедитесь, что все пути кода возвращают соединения (обработка ошибок и исключений)
  • Координируйте размеры пулов при запуске нескольких экземпляров приложения
  • Тестируйте отказоустойчивость: что происходит при перезапуске БД?
  • Держите транзакции короткими для максимальной доступности соединений

Заключение

Пулирование соединений — одна из самых эффективных и простых в реализации оптимизаций для приложений, работающих с базами данных. Оно снижает задержку, увеличивает пропускную способность и защищает БД от «штормов» соединений. Выберите правильную стратегию для вашего стека, тщательно настройте размеры пулов, мониторьте насыщение и избегайте утечек. Результат будет немедленным и измеримым.

Проверьте ваш сайт прямо сейчас

Проверить свой сайт →
Другие статьи: Инфраструктура
Инфраструктура
Оптимизация производительности Nginx: ключевые настройки
16.03.2026 · 148 просм.
Инфраструктура
Стратегии версионирования API: URL, заголовки и параметры запроса
16.03.2026 · 165 просм.
Инфраструктура
Что такое CDN и как он ускоряет сайт
11.03.2026 · 169 просм.
Инфраструктура
Алгоритмы балансировки нагрузки: Round Robin, Least Connections и другие
16.03.2026 · 165 просм.