Pool di connessioni con mssql-python

Il pool di connessioni migliora le prestazioni delle applicazioni riutilizzando le connessioni al database invece di crearne di nuove per ogni richiesta. Aprire una connessione comporta diversi passaggi che richiedono molto tempo:

  • Il driver instaura una presa di rete.
  • Il driver completa la stretta di mano TLS.
  • Il driver si autentica con il server.
  • Il driver valida i parametri di connessione.

Il pool di connessione mantiene le connessioni aperte e disponibili per il riutilizzo, così la tua app non deve ripetere questi passaggi per ogni richiesta.

Comportamento predefinito

Il pool di connessioni è abilitato di default quando crei la tua prima connessione. Le impostazioni predefinite sono le seguenti:

Setting Valore predefinito Descrizione
max_size 100 Connessioni massime per ogni stringa di connessione univoca.
idle_timeout 600 secondi (10 minuti) Numero di secondi prima che le connessioni inattive vengano chiuse.
import mssql_python

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

Configurare il pool di connessioni

Configura il pooling prima di creare qualsiasi connessione:

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

La pooling() funzione accetta i parametri seguenti:

Parametro TIPO Impostazione predefinita Descrizione
max_size int 100 Numero massimo di connessioni nel pool per stringa di connessione.
idle_timeout int 600 Pochi secondi prima che le connessioni inattive vengano sfrattate dalla piscina.
enabled Bool True Abilita o disabilita il pooling.

Disabilita il pool di connessioni

Per disabilitare il pooling, chiama pooling() con enabled=False prima di creare le connessioni:

import mssql_python

mssql_python.pooling(enabled=False)

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

Note

Imposta la configurazione del pool prima di stabilire qualsiasi connessione. Chiamare pooling() dopo aver creato connessioni non ha alcun effetto.

Come funziona il pooling

Isolamento della stringa di connessione

Ogni stringa di connessione distinta mantiene un proprio pool separato. I pool non condividono le connessioni tra diverse stringhe di connessione:

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

Ciclo di vita della connessione

Acquisire (ottenere una connessione):

  1. Il pool rimuove le connessioni obsolete (scadute per inattività).
  2. Il pool cerca di riutilizzare una connessione esistente:
    • Controlla se la connessione è attiva.
    • Resetta lo stato della connessione.
    • Se entrambi i controlli hanno successo, la connessione viene restituita.
  3. Se non esiste una connessione riutilizzabile e il pool è sotto max_size, il driver crea una nuova connessione.
  4. Se il pool ha raggiunto la capacità massima e non sono presenti connessioni valide, il driver restituisce un errore.

Rilascio (restituendo una connessione):

  1. Se il pool ha capacità disponibile, memorizza la connessione per riutilizzarla.
  2. Se il pool è a max_size, il driver chiude immediatamente la connessione.

Controlli di salute della connessione

Il driver effettua controlli di salute della connessione prima di riutilizzare una connessione pooled.

  1. Controllo in vivo: Assicura che la connessione di rete sia ancora valida.
  2. Controllo reset: Resetta lo stato della sessione (livello di isolamento, impostazioni) per un riutilizzo pulito.

Se uno dei due controlli fallisce, il pool scarta la connessione e ne crea una nuova.

Pulizia automatica

  • Timeout di inattività: Il driver chiude le connessioni rimaste inattive più a lungo del valore idle_timeout.
  • Uscita del processo: Un atexit handler chiude tutte le connessioni aggregate quando il processo Python esce.

Procedure consigliate

Dimensiona la piscina in modo appropriato

Adegua la dimensione del tuo pool alla concorrenza della tua applicazione.

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

Usa i gestori di contesto

I gestori di contesto garantiscono la corretta restituzione delle connessioni al 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

Mantieni le stringhe di connessione coerenti

Parametri diversi nelle stringhe di connessione creano pool separati.

# 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

Considera i limiti di connessione di Azure SQL

database SQL di Azure applica i limiti di connessione in base al livello di servizio. I seguenti valori sono approssimativi; Controlla la documentazione collegata per i limiti attuali:

Livello di servizio Numero massimo di connessioni simultanee
Basic 30
S0-S2 standard 60-120
Standard S3 e versioni successive 200
Premium 500

Misura il tuo max_size valore sotto questi limiti.

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

Regola il timeout di inattività del tuo carico di lavoro

  • Connessioni frequenti: Usa un valore più lungo idle_timeout per mantenere le connessioni calde.
  • Connessioni sporadiche: Usa un valore più idle_timeout breve per rilasciare risorse.
# 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

Limitazioni

L'attuale implementazione presenta alcune limitazioni rispetto ad altri driver:

Feature Condizione
ClearPool() / ClearAllPools() Non disponibile.
Statistiche/monitoraggio dei pool Non disponibile.
Sovrascrittura del pool per connessione Non disponibile.
Dimensione minima della piscina Non configurabile.

Esempio: schema di applicazione web

Il seguente esempio di Flask mostra come le connessioni vengono aggregate in modo trasparente tra le richieste:

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()

Riconosci la stanchezza in piscina

Quando tutte le connessioni nella piscina sono in uso e richiedi una nuova connessione, si vedono sintomi come:

  • Le connessioni si bloccano o si interrompono mentre aspettano una connessione gratuita.
  • La produttività dell'applicazione cala improvvisamente sotto carico.
  • L'uso della memoria aumenta man mano che il driver crea connessioni che non può riutilizzare.

Cause comuni:

  • Le connessioni non vengono restituite alla piscina. Chiudi sempre le connessioni quando hai finito, oppure usa i gestori di contesto. Una connessione che non è chiusa resta chiusa.
  • La piscina è troppo piccola per il carico di lavoro. Se hai 50 richieste concorrenti ma max_size=20, 30 richieste aspettano.
  • Le query di lunga data mantengono connessioni. Interrompi le operazioni lunghe o usa connessioni dedicate per il lavoro a batch.

Come correggere:

# 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)