Agregação de ligações com mssql-python

O pooling de ligações melhora o desempenho das aplicações ao reutilizar ligações à base de dados em vez de criar novas para cada pedido. Abrir uma ligação envolve vários passos demorados:

  • O controlador cria um socket de rede.
  • O driver completa o aperto de mão TLS.
  • O driver autentica-se com o servidor.
  • O driver valida os parâmetros da ligação.

O pooling de ligações mantém as ligações abertas e disponíveis para reutilização, por isso a sua aplicação não precisa de repetir estes passos para cada pedido.

Comportamento padrão

O agrupamento de ligações está ativado por defeito quando cria a sua primeira ligação. As configurações padrão são:

Setting Valor predefinido Descrição
max_size 100 Número máximo de ligações por cada cadeia de ligação única.
idle_timeout 600 segundos (10 minutos) Número de segundos antes de as ligações inativas serem fechadas.
import mssql_python

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

Configurar o agrupamento de conexões

Configure o pooling antes de criar qualquer ligaçã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 ligações agrupadas por cadeia de ligação.
idle_timeout int 600 Segundos antes de as conexões inativas serem removidas do pool.
enabled bool Verdade Ativar ou desativar o pooling.

Desativar o agrupamento de ligações

Para desativar o agrupamento, 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 de agrupamento antes de estabelecer qualquer conexão. Chamar pooling() depois de criar ligações não tem efeito.

Como funciona o agrupamento

Isolamento da corda de ligação

Cada cadeia de ligação única mantém o seu próprio pool independente. Os pools não partilham ligações entre diferentes cadeias de ligações:

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

Ciclo de vida da ligação

Obter (estabelecer uma ligação):

  1. O conjunto remove ligações expiradas por inatividade.
  2. A piscina tenta reutilizar uma ligação existente:
    • Verifica se a ligação está ativa.
    • Reinicia o estado da ligação.
    • Se ambos os testes tiverem sucesso, a ligação retorna.
  3. Se não existir nenhuma ligação reutilizável e o pool estiver abaixo de max_size, o driver cria uma nova ligação.
  4. Se o pool estiver com capacidade máxima e sem ligações válidas, o driver gera um erro.

Libertar (devolver uma conexão):

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

Verificações do estado da ligação

O controlador efetua verificações do estado da ligação antes de voltar a utilizar uma ligação do conjunto.

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

Se algum dos testes falhar, o pool descarta a ligação e cria uma nova.

Limpeza automática

  • Tempo de espera de inatividade: O driver fecha ligações que não são utilizadas por mais tempo do que o idle_timeout valor.
  • Saída do processo: Um atexit processador fecha todas as ligações agrupadas quando o processo Python termina.

Melhores práticas

Dimensione a sua piscina de forma adequada

Ajuste o tamanho do seu grupo 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

Utilizar gestores de contexto

Os gestores de contexto garantem que as ligações sejam corretamente devolvidas 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 cadeias de ligação consistentes

Parâmetros diferentes nas cadeias de ligaçã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 ligação do SQL do Azure

Base de Dados SQL do Azure impõe limites de ligação com base no nível de serviço. Os valores seguintes são aproximados; Consulte a documentação ligada para os limites atuais:

Escalão de serviço Máximo de conexões simultâneas
Básico 30
Padrão S0-S2 60-120
Standard S3 e versões posteriores 200
Premium 500

Reduza o valor max_size abaixo destes limites.

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

Ajuste o tempo limite de inatividade da sua carga de trabalho

  • Ligações frequentes: Use um valor mais longo idle_timeout para manter as ligações quentes.
  • Ligações esporádicas: Use um valor mais idle_timeout curto para libertar 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 tem algumas limitações em comparação com outros drivers:

Feature Situação
ClearPool() / ClearAllPools() Não disponível.
Estatísticas e monitorização do pool Não disponível.
Override por pool de ligação Não disponível.
Tamanho mínimo da piscina Não é configurável.

Exemplo: padrão de aplicação web

O exemplo seguinte do Flask mostra como as ligações são agrupadas de forma transparente entre pedidos:

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 ligações da piscina estão em uso e solicita uma nova ligação, observa sintomas como:

  • As ligações ficam suspensas ou apagam enquanto se espera por uma ligação gratuita.
  • A taxa de processamento da aplicação cai subitamente sob carga.
  • O consumo de memória aumenta à medida que o driver cria ligações que não consegue reutilizar.

Causas comuns:

  • As ligações não são devolvidas ao pool. Fecha sempre as ligações quando terminares, ou usa gestores de contexto. Uma ligação que não está fechada mantém-se verificada.
  • A piscina é demasiado pequena para a carga de trabalho. Se tiver 50 solicitações simultâneas, mas se max_size=20, 30 solicitações ficam em espera.
  • Consultas de longa duração mantêm ligações. Divida as operações longas em partes ou use ligaçõ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)