Pooling de conexões com mssql-python

O pooling de conexões melhora o desempenho das aplicações ao reutilizar conexões de banco de dados em vez de criar novas para cada solicitação. Abrir uma conexão envolve múltiplas etapas demoradas:

  • O driver estabelece um socket de rede.
  • O driver completa o handshake TLS.
  • O driver autentica com o servidor.
  • O driver valida os parâmetros da conexão.

O pool de conexão mantém as conexões abertas e disponíveis para reutilização, então seu app não precisa repetir esses passos para cada solicitação.

Comportamento padrão

O pool de conexões está ativado por padrão quando você cria sua primeira conexão. As configurações padrão são:

Setting Valor padrão Descrição
max_size 100 Número máximo de conexões para cada cadeia de conexão distinta.
idle_timeout 600 segundos (10 minutos) Número de segundos antes do encerramento das conexões ociosas.
import mssql_python

# Pooling is automatically enabled with defaults
conn = mssql_python.connect(connection_string)

Configurar o pool de conexões

Configure o pooling antes de criar qualquer conexão:

import mssql_python

# Configure custom pool settings
mssql_python.pooling(max_size=50, idle_timeout=300)

# Now create connections
conn = mssql_python.connect(connection_string)

Parameters

A pooling() função aceita os seguintes parâmetros:

Parâmetro Tipo Default Descrição
max_size int 100 Número máximo de conexões no pool por cadeia de conexão.
idle_timeout int 600 Segundos antes das conexões ociosas serem despejadas da piscina.
enabled bool Verdade Ative ou desative o agrupamento.

Desativar o pooling de conexões

Para desativar o pooling, chame pooling() com enabled=False antes de criar conexões:

import mssql_python

mssql_python.pooling(enabled=False)

# Connections are now created and destroyed per use
conn = mssql_python.connect(connection_string)

Note

Defina a configuração do pooling antes de estabelecer qualquer conexão. Ligar pooling() após criar conexões não tem efeito.

Como funciona o agrupamento

Isolamento da corda de conexão

Cada cadeia de conexão única mantém seu próprio pool de conexões independente. Pools não compartilham conexões entre diferentes strings de conexão:

# These use separate pools
conn1 = mssql_python.connect("Server=<server1>;Database=<database1>;...")
conn2 = mssql_python.connect("Server=<server2>;Database=<database2>;...")

Ciclo de vida da conexão

Obter (estabelecendo uma conexão):

  1. A piscina remove conexões obsoletas (paradas e expiradas).
  2. O pool tenta reutilizar uma conexão existente:
    • Ele verifica se a conexão está ativa.
    • Ele reinicia o estado da conexão.
    • Se ambos os testes tiverem sucesso, a conexão retorna.
  3. Se não houver conexão reutilizável e o pool estiver abaixo de max_size, o driver cria uma nova conexão.
  4. Se o pool atingir a capacidade máxima e não houver conexões válidas, o driver gera um erro.

Liberação (retornando uma conexão):

  1. Se a piscina tiver capacidade, ela armazena a conexão para reutilização.
  2. Se o pool estiver em max_size, o driver fecha a conexão imediatamente.

Verificações de saúde da conexão

O driver realiza verificações de integridade da conexão antes de reutilizar uma conexão do pool.

  1. Verificação de disponibilidade: Garante que a conexão de rede continua válida.
  2. Verificação de reset: Reinicia o estado da sessão (nível de isolamento, configurações) para reutilização limpa.

Se qualquer uma das verificações falhar, o pool descarta a conexão e cria uma nova.

Limpeza automática

  • Tempo limite de inatividade: O driver fecha conexões que permanecem sem uso por mais tempo que o valor de idle_timeout.
  • Encerramento do processo: Um atexit manipulador fecha todas as conexões agrupadas quando o processo Python é encerrado.

Práticas recomendadas

Dimensione sua piscina de forma adequada

Ajuste o tamanho do pool de acordo com a concorrência da sua aplicação.

# For a web application with 20 concurrent requests
mssql_python.pooling(max_size=25)  # Slightly more than expected concurrency

Uso de gerentes de contexto

Os gerentes de contexto garantem que você retorne corretamente as conexões ao pool.

with mssql_python.connect(connection_string) as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT TOP 5 Name, ListPrice FROM Production.Product")
    rows = cursor.fetchall()
# Connection returned to pool

Mantenha as cordas de conexão consistentes

Parâmetros diferentes nas strings de conexão criam pools separados.

# These create THREE separate pools (inefficient)
conn1 = mssql_python.connect("Server=<server>;Database=<database>;Encrypt=yes;")
conn2 = mssql_python.connect("SERVER=<server>;DATABASE=<database>;ENCRYPT=yes;")  # Different case
conn3 = mssql_python.connect("Server=<server>;Database=<database>;Encrypt=yes;", timeout=30)  # Extra parameter

# Use a constant connection string instead
CONNECTION_STRING = "Server=<server>;Database=<database>;Encrypt=yes;"
conn1 = mssql_python.connect(CONNECTION_STRING)
conn2 = mssql_python.connect(CONNECTION_STRING)  # Same pool

Considere os limites de conexão do SQL do Azure

Banco de Dados SQL do Azure aplica limites de conexão com base no nível de serviço. Os valores a seguir são aproximados; Confira a documentação vinculada para os limites atuais:

Nível de serviço Máximo de conexões simultâneas
Básico 30
Padrão S0-S2 60-120
Versão padrão S3 e posteriores 200
Premium 500

Reduza seu max_size valor abaixo desses limites.

# For Azure SQL Standard S2 (120 limit)
mssql_python.pooling(max_size=100)  # Leave headroom

Ajuste o tempo limite de inatividade para sua carga de trabalho

  • Contatos frequentes: Use um valor mais longo idle_timeout para manter as conexões quentes.
  • Conexões esporádicas: Use um valor mais idle_timeout curto para liberar recursos.
# High-frequency API: keep connections warm
mssql_python.pooling(idle_timeout=1800)  # 30 minutes

# Batch job running every hour: release between runs
mssql_python.pooling(idle_timeout=60)  # 1 minute

Limitations

A implementação atual apresenta algumas limitações em comparação com outros drivers:

Característica Status
ClearPool() / ClearAllPools() Não disponível.
Estatísticas/monitoramento do pool Não disponível.
Override por pool de conexão Não disponível.
Tamanho mínimo da piscina Não configurável.

Exemplo: padrão de aplicação web

O exemplo a seguir do Flask mostra como as conexões são agrupadas de forma transparente entre as requisições:

import mssql_python
from flask import Flask, g

app = Flask(__name__)

# Configure pooling at startup
mssql_python.pooling(max_size=20, idle_timeout=300)

def get_db():
    if 'db' not in g:
        g.db = mssql_python.connect(app.config['DATABASE_URL'])
    return g.db

@app.teardown_appcontext
def close_db(error):
    db = g.pop('db', None)
    if db is not None:
        db.close()  # Returns to pool

@app.route('/products')
def list_products():
    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("SELECT TOP 5 Name, ListPrice FROM Production.Product")
    return cursor.fetchall()

Reconhecer o cansaço na piscina

Quando todas as conexões da piscina estão em uso e você solicita uma nova conexão, você vê sintomas como:

  • As conexões ficam travadas ou expiram enquanto aguardam uma conexão disponível.
  • A taxa de processamento da aplicação cai repentinamente quando submetida a carga.
  • O uso de memória aumenta à medida que o driver cria conexões que não pode reutilizar.

Causas comuns:

  • As conexões não são retornadas ao pool. Sempre feche as conexões quando terminar, ou use gerenciadores de contexto. Uma conexão que não está fechada permanece desligada.
  • Pool muito pequeno para a carga de trabalho. Se você tiver 50 solicitações simultâneas, mas max_size=20, 30 solicitações ficam esperando.
  • Consultas de longa duração mantêm conexões. Divida operações longas ou use conexões dedicadas para processamento em lote.

Como corrigir:

# 1. Always use context managers to guarantee return
with mssql_python.connect(connection_string) as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT ...")
    rows = cursor.fetchall()
# Connection returned to pool here, even if an exception occurs

# 2. Size the pool to match your concurrency
mssql_python.pooling(max_size=50)  # Match or slightly exceed expected concurrent connections

# 3. Reduce idle timeout if connections go stale
mssql_python.pooling(idle_timeout=120)