Cloud e plataforma

Pooling de Conexões de Banco de Dados: Melhores Práticas de Produção para 2026

Connection pooling é uma das otimizações de performance mais impactantes para aplicações baseadas em banco de dados. Configuração adequada previne exaustão de conexões, reduz latência e melhora throughput geral do sistema.

13/03/20267 min de leituraCloud
Pooling de Conexões de Banco de Dados: Melhores Práticas de Produção para 2026

Resumo executivo

Connection pooling é uma das otimizações de performance mais impactantes para aplicações baseadas em banco de dados. Configuração adequada previne exaustão de conexões, reduz latência e melhora throughput geral do sistema.

Ultima atualizacao: 13/03/2026

Introdução: O assassino oculto de performance

Conexões de banco de dados são recursos caros. Estabelecer uma nova conexão requer round-trips de rede, handshakes de autenticação e alocação de recursos. Em sistemas de alto throughput, abrir uma nova conexão para cada request cria latência inaceitável e pode sobrecarregar infraestrutura de banco de dados.

Connection pooling mitiga esse custo mantendo um conjunto de conexões pré-estabelecidas que podem ser reutilizadas através de requests. Quando configurado adequadamente, connection pooling reduz latência em 10-100x, aumenta throughput significativamente e fornece resiliência contra tempestades de conexão.

No entanto, connection pooling é frequentemente mal configurado. Superprovisionamento desperdiça memória, subprovisionamento causa contenção, e tratamento inadequado de timeout cria falhas em cascata. Este post cobre estratégias testadas em produção para otimizar pools de conexão.

Fundamentos de pool de conexão

Ciclo de vida de conexão

Compreender o ciclo de vida de uma conexão em pool é essencial para configuração adequada:

┌─────────────────────────────────────────────────────────────────┐
│                    Connection Pool                             │
├─────────────────────────────────────────────────────────────────┤
│                                                            │
│  ┌──────────────┐    Borrow    ┌──────────────────────┐    │
│  │ Idle Conn A  │ ──────────> │  Active Conn 1     │    │
│  └──────────────┘              │  (in use)          │    │
│                                 └──────────────────────┘    │
│                                                            │
│  ┌──────────────┐    Borrow    ┌──────────────────────┐    │
│  │ Idle Conn B  │ ──────────> │  Active Conn 2     │    │
│  └──────────────┘              │  (in use)          │    │
│                                 └──────────────────────┘    │
│                                                            │
│  ┌──────────────┐                ┌──────────────────────┐    │
│  │ Idle Conn C  │                │  Active Conn 3     │    │
│  └──────────────┘                │  (in use)          │    │
│                                 └──────────────────────┘    │
│                         Return                              │
│  ┌──────────────────────────────────────────────────────┐    │
└──┴──────────────────────────────────────────────────────┴──┘

Estágios do ciclo de vida:

  1. Inicialização: Pool cria e pré-aquece conexões
  2. Empréstimo: Aplicação solicita uma conexão do pool
  3. Uso ativo: Aplicação executa queries
  4. Retorno: Conexão retornada ao pool (não fechada)
  5. Validação: Pool valida conexão antes de reutilização
  6. Evicção: Conexões antigas ou obsoletas removidas

Tipos de pool

Pools thread-local mantêm um pool separado por thread:

typescriptclass ThreadLocalConnectionPool {
  private threadLocal: Map<Thread, Connection> = new Map();

  borrow(threadId: string): Connection {
    let connection = this.threadLocal.get(threadId);

    if (!connection) {
      connection = this.createConnection();
      this.threadLocal.set(threadId, connection);
    }

    return connection;
  }

  return(connection: Connection): void {
    // Conexão permanece em armazenamento thread-local
    // Não retornada ao pool global
  }
}

Pools compartilhados mantêm conexões compartilhadas entre threads:

typescriptclass SharedConnectionPool {
  private available: Connection[] = [];
  private active: Set<Connection> = new Set();
  private maxConnections: number;

  constructor(maxConnections: number) {
    this.maxConnections = maxConnections;
  }

  async borrow(): Promise<Connection> {
    if (this.available.length > 0) {
      const connection = this.available.pop()!;
      this.active.add(connection);
      return connection;
    }

    if (this.active.size >= this.maxConnections) {
      throw new Error('Connection pool exaurido');
    }

    const connection = await this.createConnection();
    this.active.add(connection);
    return connection;
  }

  return(connection: Connection): void {
    this.active.delete(connection);

    if (this.available.length < this.maxConnections - this.active.size) {
      this.available.push(connection);
    } else {
      connection.close(); // Fecha se pool está cheio
    }
  }
}

Estratégias de dimensionamento de pool

A fórmula

Fórmulas tradicionais de dimensionamento frequentemente superprovisionam pools. O tamanho ótimo depende de:

typescriptinterface PoolSizeParameters {
  // Características do banco de dados
  cpuCores: number;           // CPU cores do banco
  diskIO: number;              // Capacidade de I/O (IOPS)
  memoryPerConnection: number;   // MB por conexão

  // Características da aplicação
  activeQueries: number;         // Queries concorrentes
  queryLatency: number;        // Latência média de query (ms)
  targetLatency: number;       // Latência alvo (ms)

  // Modelo de concorrência
  blocking: boolean;           // Queries são bloqueantes?
  async: boolean;              // I/O é assíncrono?
}

function calculateOptimalPoolSize(params: PoolSizeParameters): number {
  // Para workloads limitados por CPU:
  if (params.queryLatency < params.targetLatency * 0.5) {
    // Query é rápida o suficiente que CPU se torna bottleneck
    return params.cpuCores * 2;
  }

  // Para workloads limitados por I/O:
  if (params.blocking && !params.async) {
    // I/O bloqueante permite mais conexões por core
    return params.cpuCores * 4;
  }

  // Para workloads assíncronos:
  if (params.async) {
    // Async permite muito mais conexões
    return Math.min(
      params.cpuCores * 10,
      Math.floor(params.activeQueries * 1.5)
    );
  }

  // Dimensionamento conservador padrão
  return params.cpuCores * 2;
}

Dimensionamento baseado em memória

Calcule tamanho máximo de pool baseado em memória disponível:

typescriptfunction calculateMemoryBasedPoolSize(
  availableMemoryMB: number,
  memoryPerConnectionMB: number,
  safetyMargin: number = 0.8 // Usa 80% da memória disponível
): number {
  const usableMemory = availableMemoryMB * safetyMargin;
  const maxPoolSize = Math.floor(usableMemory / memoryPerConnectionMB);

  // Tamanho mínimo de pool para confiabilidade
  const minPoolSize = 4;

  return Math.max(minPoolSize, maxPoolSize);
}

// Exemplo: estimativa de memória de conexão PostgreSQL
interface PostgreSQLConnectionMemory {
  workMem: number;      // working memory (MB)
  maintenanceWorkMem: number; // maintenance memory (MB)
  sharedBuffers: number;   // shared buffers (MB)
}

function estimatePostgresConnectionMemory(
  config: PostgreSQLConnectionMemory
): number {
  // Cada conexão usa work_mem para sorts/hashes
  // Mais share de shared_buffers
  return config.workMem + (config.sharedBuffers / 100);
}

// Exemplo de dimensionamento
const memoryConfig = {
  workMem: 64,              // 64MB por conexão
  maintenanceWorkMem: 512,    // 512MB total
  sharedBuffers: 4096,         // 4GB total
};

const memoryPerConnection = estimatePostgresConnectionMemory(memoryConfig);
const serverMemory = 32768; // 32GB servidor
const poolSize = calculateMemoryBasedPoolSize(serverMemory, memoryPerConnection);

console.log(`Tamanho ótimo de pool: ${poolSize}`);

Dimensionamento baseado em carga

Ajuste tamanho de pool baseado em carga atual:

typescriptclass DynamicConnectionPool {
  private pool: ConnectionPool;
  private minSize: number;
  private maxSize: number;
  private currentSize: number;
  private metrics: PoolMetrics;

  constructor(minSize: number, maxSize: number) {
    this.minSize = minSize;
    this.maxSize = maxSize;
    this.currentSize = minSize;
    this.pool = new ConnectionPool(minSize);
    this.metrics = new PoolMetrics();
  }

  async adapt(): Promise<void> {
    const metrics = await this.metrics.collect();

    // Calcula tamanho alvo baseado em carga
    const targetSize = this.calculateTargetSize(metrics);

    // Ajusta tamanho de pool gradualmente
    if (targetSize > this.currentSize) {
      await this.scaleUp(targetSize);
    } else if (targetSize < this.currentSize) {
      await this.scaleDown(targetSize);
    }
  }

  private calculateTargetSize(metrics: PoolMetrics): number {
    const utilization = metrics.activeConnections / metrics.totalConnections;
    const waitTime = metrics.averageWaitTime;
    const targetLatency = metrics.targetLatency;

    // Se esperando muito tempo, aumenta pool
    if (waitTime > targetLatency * 0.5) {
      return Math.min(this.maxSize, this.currentSize * 1.5);
    }

    // Se utilização baixa, diminui pool
    if (utilization < 0.3 && this.currentSize > this.minSize) {
      return Math.max(this.minSize, Math.floor(this.currentSize * 0.8));
    }

    return this.currentSize;
  }

  private async scaleUp(targetSize: number): Promise<void> {
    while (this.currentSize < targetSize && this.currentSize < this.maxSize) {
      await this.pool.addConnection();
      this.currentSize++;
    }
  }

  private async scaleDown(targetSize: number): Promise<void> {
    while (this.currentSize > targetSize && this.currentSize > this.minSize) {
      await this.pool.removeConnection();
      this.currentSize--;
    }
  }
}

Configuração de timeout

Timeout de conexão

typescriptinterface ConnectionPoolConfig {
  // Estabelecimento de conexão
  connectTimeoutMs: number;        // Timeout para novas conexões
  socketTimeoutMs: number;          // Timeout de leitura de socket

  // Operações de pool
  acquireTimeoutMs: number;         // Timeout para emprestar conexão
  idleTimeoutMs: number;            // Timeout antes de fechar conexões idle
  maxLifetimeMs: number;            // Tempo de vida máximo da conexão

  // Validação
  validationIntervalMs: number;      // Intervalo para validação de conexão
  validationTimeoutMs: number;      // Timeout para query de validação
}

function validatePoolConfig(config: ConnectionPoolConfig): void {
  // Timeout de conexão deve ser significativamente maior que timeout de query
  if (config.socketTimeoutMs < config.connectTimeoutMs) {
    throw new Error('Socket timeout deve ser >= connection timeout');
  }

  // Acquire timeout deve ser curto para prevenir falhas em cascata
  if (config.acquireTimeoutMs > 5000) {
    console.warn('Acquire timeout longo pode causar falhas em cascata');
  }

  // Idle timeout deve ser razoável
  if (config.idleTimeoutMs < 30000) {
    console.warn('Idle timeout < 30s pode causar churn excessivo');
  }
}

// Configuração de produção
const productionConfig: ConnectionPoolConfig = {
  connectTimeoutMs: 5000,      // 5 segundos para estabelecer conexão
  socketTimeoutMs: 30000,      // 30 segundos timeout de socket
  acquireTimeoutMs: 1000,       // 1 segundo para emprestar conexão
  idleTimeoutMs: 600000,        // 10 minutos antes de fechar idle
  maxLifetimeMs: 3600000,       // 1 hora tempo de vida máximo de conexão
  validationIntervalMs: 30000,   // Validar a cada 30 segundos
  validationTimeoutMs: 5000,     // 5 segundo timeout de validação
};

Timeout de query

typescriptclass ConnectionWithQueryTimeout {
  async executeQuery(
    connection: Connection,
    query: string,
    timeoutMs: number
  ): Promise<any> {
    const queryPromise = connection.execute(query);
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Query timeout')), timeoutMs);
    });

    try {
      const result = await Promise.race([queryPromise, timeoutPromise]);
      return result;
    } catch (error) {
      if (error.message === 'Query timeout') {
        // Mata a query no lado do servidor
        await connection.cancelQuery();

        // Retorna conexão ao pool (pode precisar de validação)
        this.validateConnection(connection);

        throw error;
      }
      throw error;
    }
  }

  async validateConnection(connection: Connection): Promise<void> {
    try {
      // Query de validação rápida
      await connection.execute('SELECT 1');
    } catch (error) {
      // Conexão quebrada, feche
      connection.close();
      throw error;
    }
  }
}

Validação de conexão

Validação baseada em ping

typescriptclass PingValidatingPool {
  private pool: ConnectionPool;
  private validationIntervalMs: number;

  constructor(pool: ConnectionPool, validationIntervalMs: number) {
    this.pool = pool;
    this.validationIntervalMs = validationIntervalMs;
    this.startValidationLoop();
  }

  private startValidationLoop(): void {
    setInterval(async () => {
      await this.validateAllConnections();
    }, this.validationIntervalMs);
  }

  private async validateAllConnections(): Promise<void> {
    const connections = this.pool.getAllConnections();

    for (const connection of connections) {
      if (!connection.isActive) {
        await this.validateConnection(connection);
      }
    }
  }

  private async validateConnection(connection: Connection): Promise<void> {
    try {
      const startTime = Date.now();
      await connection.ping();
      const latency = Date.now() - startTime;

      // Atualiza métricas de conexão
      connection.setLatency(latency);
      connection.setLastValidated(Date.now());

      // Fecha conexões com latência alta
      if (latency > 10000) { // 10 segundos
        console.warn(`Fechando conexão lenta (${latency}ms)`);
        connection.close();
      }
    } catch (error) {
      console.warn(`Validação de conexão falhou: ${error.message}`);
      connection.close();
    }
  }
}

Validação baseada em query

typescriptclass QueryValidatingPool {
  private pool: ConnectionPool;
  private validationQuery: string;

  constructor(pool: ConnectionPool, validationQuery: string = 'SELECT 1') {
    this.pool = pool;
    this.validationQuery = validationQuery;
  }

  async borrow(): Promise<Connection> {
    const connection = await this.pool.borrow();

    // Valida antes de retornar
    const isValid = await this.validate(connection);

    if (!isValid) {
      // Conexão está obsoleta, obtenha nova
      connection.close();
      return await this.pool.borrow();
    }

    return connection;
  }

  private async validate(connection: Connection): Promise<boolean> {
    try {
      await connection.execute(this.validationQuery);
      return true;
    } catch (error) {
      console.warn(`Validação de conexão falhou: ${error.message}`);
      return false;
    }
  }
}

Gerenciamento de ciclo de vida de conexão

Padrões de retorno adequados

typescript// ❌ RUIM: Não retornando conexão
async function badPattern(userId: string): Promise<User> {
  const connection = await pool.borrow();
  const user = await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
  // Conexão não retornada ao pool!
  return user;
}

// ❌ RUIM: Retornando em bloco try (risco de exceção)
async function badPatternTry(userId: string): Promise<User> {
  const connection = await pool.borrow();
  try {
    const user = await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
    pool.return(connection); // Retornada antes da query completar!
    return user;
  } catch (error) {
    pool.return(connection);
    throw error;
  }
}

// ✅ BOM: Usando try-finally
async function goodPattern(userId: string): Promise<User> {
  const connection = await pool.borrow();
  try {
    return await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
  } finally {
    pool.return(connection);
  }
}

// ✅ BOM: Usando padrão de recurso assíncrono
async function usingConnection<T>(
  callback: (conn: Connection) => Promise<T>
): Promise<T> {
  const connection = await pool.borrow();
  try {
    return await callback(connection);
  } finally {
    pool.return(connection);
  }
}

// Uso
const user = await usingConnection(async (conn) => {
  return await conn.query('SELECT * FROM users WHERE id = ?', [userId]);
});

Tratamento de transação

typescriptclass TransactionAwarePool {
  private pool: ConnectionPool;

  async withTransaction<T>(
    callback: (conn: Connection) => Promise<T>
  ): Promise<T> {
    const connection = await this.pool.borrow();

    try {
      await connection.beginTransaction();

      const result = await callback(connection);

      await connection.commit();

      this.pool.return(connection);
      return result;
    } catch (error) {
      try {
        await connection.rollback();
      } catch (rollbackError) {
        console.error('Rollback falhou:', rollbackError);
      }

      this.pool.return(connection);
      throw error;
    }
  }

  async withSavepoint<T>(
    callback: (conn: Connection) => Promise<T>,
    savepointName: string = 'sp1'
  ): Promise<T> {
    const connection = await this.pool.borrow();

    try {
      await connection.createSavepoint(savepointName);

      const result = await callback(connection);

      await connection.releaseSavepoint(savepointName);

      this.pool.return(connection);
      return result;
    } catch (error) {
      try {
        await connection.rollbackToSavepoint(savepointName);
      } catch (rollbackError) {
        console.error('Rollback para savepoint falhou:', rollbackError);
      }

      this.pool.return(connection);
      throw error;
    }
  }
}

// Uso
await pool.withTransaction(async (conn) => {
  const user = await conn.query('SELECT * FROM users WHERE id = ?', [userId]);
  await conn.query('INSERT INTO audit_log (action, user_id) VALUES (?, ?)', ['update', userId]);
  return user;
});

Monitoramento e observabilidade

Métricas de pool

typescriptclass ConnectionPoolMetrics {
  private metrics: PoolMetricsData;

  constructor(private pool: ConnectionPool) {
    this.metrics = this.initializeMetrics();
    this.startMetricCollection();
  }

  private initializeMetrics(): PoolMetricsData {
    return {
      totalConnections: 0,
      activeConnections: 0,
      idleConnections: 0,
      waitingRequests: 0,
      totalBorrows: 0,
      failedBorrows: 0,
      averageWaitTime: 0,
      maxWaitTime: 0,
      connectionLifetime: new Map<string, number>(),
      connectionUsageCount: new Map<string, number>(),
    };
  }

  recordBorrow(waitTimeMs: number, success: boolean): void {
    this.metrics.totalBorrows++;

    if (success) {
      this.metrics.averageWaitTime =
        (this.metrics.averageWaitTime * (this.metrics.totalBorrows - 1) + waitTimeMs) /
        this.metrics.totalBorrows;
      this.metrics.maxWaitTime = Math.max(this.metrics.maxWaitTime, waitTimeMs);
    } else {
      this.metrics.failedBorrows++;
    }
  }

  recordConnectionLifecycle(connectionId: string, event: 'create' | 'close'): void {
    const now = Date.now();

    if (event === 'create') {
      this.metrics.totalConnections++;
      this.metrics.connectionLifetime.set(connectionId, now);
    } else if (event === 'close') {
      const created = this.metrics.connectionLifetime.get(connectionId) || now;
      const lifetime = now - created;
      this.metrics.connectionLifetime.delete(connectionId);
      this.metrics.totalConnections--;
    }
  }

  getMetrics(): PoolMetricsSnapshot {
    return {
      totalConnections: this.metrics.totalConnections,
      activeConnections: this.metrics.activeConnections,
      idleConnections: this.metrics.idleConnections,
      utilization: this.metrics.activeConnections / this.metrics.totalConnections,
      averageWaitTime: this.metrics.averageWaitTime,
      maxWaitTime: this.metrics.maxWaitTime,
      failedBorrowRate: this.metrics.failedBorrows / this.metrics.totalBorrows,
      averageConnectionLifetime: this.calculateAverageLifetime(),
    };
  }

  private calculateAverageLifetime(): number {
    const lifetimes = Array.from(this.metrics.connectionLifetime.values());
    if (lifetimes.length === 0) return 0;

    const now = Date.now();
    const currentLifetimes = lifetimes.map(t => now - t);

    return currentLifetimes.reduce((sum, l) => sum + l, 0) / currentLifetimes.length;
  }
}

Alertas

typescriptclass PoolHealthMonitor {
  private metrics: ConnectionPoolMetrics;
  private alerts: HealthAlert[];

  async checkHealth(): Promise<HealthStatus> {
    const metrics = this.metrics.getMetrics();
    const alerts: HealthAlert[] = [];

    // Verifica exaustão de conexão
    if (metrics.utilization > 0.95) {
      alerts.push({
        severity: 'critico',
        message: 'Connection pool quase exaurido',
        metric: 'utilization',
        value: metrics.utilization,
        threshold: 0.95,
      });
    }

    // Verifica tempos de espera altos
    if (metrics.averageWaitTime > 100) {
      alerts.push({
        severity: 'aviso',
        message: 'Tempo de espera médio alto',
        metric: 'averageWaitTime',
        value: metrics.averageWaitTime,
        threshold: 100,
      });
    }

    // Verifica taxa de falhas de borrow
    if (metrics.failedBorrowRate > 0.01) {
      alerts.push({
        severity: 'erro',
        message: 'Alta taxa de falhas de borrow',
        metric: 'failedBorrowRate',
        value: metrics.failedBorrowRate,
        threshold: 0.01,
      });
    }

    this.alerts = alerts;

    return {
      healthy: alerts.length === 0,
      alerts,
      metrics,
    };
  }
}

interface HealthAlert {
  severity: 'info' | 'aviso' | 'erro' | 'critico';
  message: string;
  metric: string;
  value: number;
  threshold: number;
}

interface HealthStatus {
  healthy: boolean;
  alerts: HealthAlert[];
  metrics: PoolMetricsSnapshot;
}

Anti-padrões comuns

Criando muitos pools

typescript// ❌ RUIM: Criando novo pool para cada request
async function badPattern(query: string): Promise<any> {
  const pool = new ConnectionPool(config);
  const connection = await pool.borrow();
  const result = await connection.query(query);
  pool.return(connection);
  return result;
}

// ✅ BOM: Reusando instância única de pool
const globalPool = new ConnectionPool(config);

async function goodPattern(query: string): Promise<any> {
  const connection = await globalPool.borrow();
  try {
    return await connection.query(query);
  } finally {
    globalPool.return(connection);
  }
}

Não retornando conexões

typescript// ❌ RUIM: Vazamento de conexão
async function createUser(userData: UserData): Promise<User> {
  const connection = await pool.borrow();
  const user = await connection.query('INSERT INTO users ...');
  // Conexão não retornada!
  return user;
}

// ✅ BOM: Retorno adequado com finally
async function createUserGood(userData: UserData): Promise<User> {
  const connection = await pool.borrow();
  try {
    return await connection.query('INSERT INTO users ...');
  } finally {
    pool.return(connection);
  }
}

Ignorando estado de conexão

typescript// ❌ RUIM: Reusando conexão sem validação
async function badPattern(): Promise<any> {
  const connection = await pool.borrow();
  return await connection.query('SELECT * FROM users');
}

// ✅ BOM: Validar antes de usar
async function goodPattern(): Promise<any> {
  const connection = await pool.borrow();
  try {
    await connection.validate();
    return await connection.query('SELECT * FROM users');
  } catch (error) {
    connection.close();
    throw error;
  }
}

Configurações específicas de tecnologia

PostgreSQL

typescriptconst pgPoolConfig: PoolConfig = {
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT || '5432'),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,

  // Dimensionamento de pool
  max: 20,                    // Conexões máximas
  min: 5,                     // Conexões mínimas
  idleTimeoutMillis: 30000,     // Fecha conexões idle após 30s

  // Timeouts
  connectionTimeoutMillis: 5000,  // 5s para estabelecer conexão
  idleTimeoutMillis: 30000,      // 30s timeout idle

  // Validação
  statement_timeout: 30000,      // 30s timeout de query

  // Ciclo de vida de conexão
  maxUses: 10000,              // Fecha após 10000 usos
  maxLifetimeMillis: 3600000,    // Fecha após 1 hora
};

const pool = new Pool(pgPoolConfig);

MySQL

typescriptconst mysqlPoolConfig: PoolOptions = {
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT || '3306'),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,

  // Dimensionamento de pool
  connectionLimit: 20,            // Conexões máximas
  queueLimit: 0,                 // Fila ilimitada (ou defina limite)

  // Timeouts
  connectTimeout: 10000,          // 10s timeout de conexão
  acquireTimeout: 10000,          // 10s para obter conexão do pool

  // Ciclo de vida de conexão
  enableKeepAlive: true,           // Mantém conexões vivas
  keepAliveInitialDelay: 0,

  // Validação
  typeCast: true,                  // Converte tipos automaticamente
  timezone: 'Z',                   // Usa UTC
};

const pool = mysql.createPool(mysqlPoolConfig);

Redis

typescriptconst redisPoolConfig: RedisOptions = {
  host: process.env.REDIS_HOST,
  port: parseInt(process.env.REDIS_PORT || '6379'),
  password: process.env.REDIS_PASSWORD,

  // Connection pooling
  maxRetriesPerRequest: 3,
  enableReadyCheck: true,
  enableOfflineQueue: false,

  // Timeouts
  connectTimeout: 5000,              // 5s timeout de conexão
  socketTimeout: 5000,               // 5s timeout de socket
  commandTimeout: 5000,               // 5s timeout de comando

  // Ciclo de vida de conexão
  keepAlive: 30000,                  // Mantém vivo a cada 30s
  maxRetriesPerRequest: 3,            // Retry 3 vezes

  // Pooling
  maxTotalClients: 20,                // Conexões máximas
  minTotalClients: 5,                 // Conexões mínimas
};

const redis = new Redis(redisPoolConfig);

Framework de decisão

Dimensionamento de pool

CenárioEstratégia de Tamanho de PoolRacional
Limitado por CPUCPU cores × 2Conexões adicionais não ajudam
I/O-bound bloqueanteCPU cores × 4I/O bloqueante permite mais por core
I/O-bound assíncronoCPU cores × 10Async permite muito mais conexões
Limitado por memóriaBaseado em memória disponívelPrevine kills OOM

Configuração de timeout

ConfiguraçãoConservadorAgressivoRecomendado
Timeout de conexão10s1s5s
Timeout de acquire5s100ms1s
Timeout idle1h30s10m
Tempo de vida máximo24h30m1h

Conclusão

Connection pooling é uma das otimizações mais impactantes para aplicações baseadas em banco de dados. Configuração adequada reduz latência em ordens de magnitude, aumenta throughput significativamente e fornece resiliência contra tempestades de conexão.

A configuração ótima depende do seu workload específico: limitado por CPU vs I/O, bloqueante vs assíncrono, restrições de memória e padrões de query. Monitore métricas de pool continuamente e ajuste configuração baseado em comportamento observado. O objetivo não é otimização teórica—é performance previsível e confiável que escala com sua aplicação.

Pergunta prática de fechamento: Qual é sua latência média de query atual, e como mudanças de configuração de connection pooling afetariam throughput e utilização de recursos do seu sistema?


Otimizando performance de banco de dados e precisa de orientação especializada sobre connection pooling e sintonia de banco? Fale com especialistas em banco de dados da Imperialis sobre implementar estratégias de connection pooling que maximizam throughput enquanto minimizam uso de recursos.

Fontes

Leituras relacionadas