← Назад к вопросам

Что такое connection pooling и зачем он нужен при работе с базой данных?

1.8 Middle🔥 231 комментариев
#Базы данных и SQL#Кэширование и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Connection Pooling при работе с базой данных

Connection pooling - это техника управления соединениями с базой данных, при которой создается и поддерживается пул заранее открытых соединений, которые переиспользуются между запросами вместо создания нового соединения на каждый запрос.

Проблема без пула

Создание TCP-соединения с базой данных - дорогая операция:

  1. TCP handshake (3 пакета)
  2. SSL/TLS handshake (еще 2-4 пакета)
  3. Аутентификация в СУБД
  4. Выделение ресурсов на стороне сервера БД

Это занимает 20-100мс на каждое соединение. При 1000 запросов в секунду без пула:

// Плохо - новое соединение на каждый запрос
async function getUser(id: string) {
  const client = new Client({ connectionString: DATABASE_URL });
  await client.connect();        // 50мс каждый раз
  const result = await client.query("SELECT * FROM users WHERE id = $1", [id]);
  await client.end();
  return result.rows[0];
}

Как работает пул

Пул поддерживает N открытых соединений. Когда приложению нужно сделать запрос:

  1. Берет свободное соединение из пула
  2. Выполняет запрос
  3. Возвращает соединение обратно в пул
import { Pool } from "pg";

const pool = new Pool({
  connectionString: DATABASE_URL,
  max: 20,
  min: 5,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 5000
});

async function getUser(id: string) {
  const result = await pool.query(
    "SELECT * FROM users WHERE id = $1", [id]
  );
  return result.rows[0];
}

Параметры пула

max (размер пула): Формула для PostgreSQL:

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

Для типичного сервера с 4 ядрами: pool.max = 10-20. Больше - не значит лучше: при 100+ соединениях PostgreSQL деградирует из-за контекстного переключения.

min: Минимум idle-соединений. Предотвращает задержку при первых запросах после периода неактивности.

idleTimeoutMillis: Время жизни неиспользуемого соединения.

Connection Pool в разных библиотеках

node-postgres (pg):

const pool = new Pool({ max: 20 });

// Автоматическое управление
await pool.query("SELECT 1");

// Ручное управление (для транзакций)
const client = await pool.connect();
try {
  await client.query("BEGIN");
  await client.query("UPDATE accounts SET balance = balance - 100 WHERE id = $1", [fromId]);
  await client.query("UPDATE accounts SET balance = balance + 100 WHERE id = $1", [toId]);
  await client.query("COMMIT");
} catch (e) {
  await client.query("ROLLBACK");
  throw e;
} finally {
  client.release(); // ОБЯЗАТЕЛЬНО вернуть в пул!
}

Prisma:

// Prisma управляет пулом автоматически
// Настройка через connection string
// postgresql://user:pass@host/db?connection_limit=20&pool_timeout=10

Проблемы и антипаттерны

  • Connection leak: забыли вызвать client.release() - соединение "утекло" из пула. Решение: всегда используйте try/finally
  • Слишком большой пул: N инстансов x M соединений = N*M соединений к БД. 10 инстансов по 20 = 200 соединений
  • Внешний пул (PgBouncer): при множестве инстансов лучше использовать внешний connection pooler

PgBouncer

Для масштабирования используют PgBouncer - внешний connection pooler:

App1 (pool=20) --+
App2 (pool=20) --+-- PgBouncer (max=50) -- PostgreSQL
App3 (pool=20) --+

Bouncer мультиплексирует 60 входящих соединений в 50 реальных к PostgreSQL, используя режимы: session, transaction или statement.