Verbindungspooling mit mssql-python

Connection Pooling verbessert die Anwendungsleistung, indem Datenbankverbindungen wiederverwendet werden, anstatt für jede Anfrage neue zu erstellen. Das Öffnen einer Verbindung erfordert mehrere zeitaufwändige Schritte:

  • Der Treiber richtet einen Netzwerksockel ein.
  • Der Fahrer beendet den TLS-Handschlag.
  • Der Fahrer authentifiziert sich beim Server.
  • Der Treiber validiert die Verbindungsparameter.

Connection Pooling hält Verbindungen offen und zur Wiederverwendung verfügbar, sodass Ihre App diese Schritte nicht für jede Anfrage wiederholen muss.

Standardverhalten

Connection Pooling ist standardmäßig aktiviert , wenn du deine erste Verbindung erstellst. Die Standardeinstellungen sind folgende:

Einstellung Standardwert Beschreibung
max_size 100 Maximale Anzahl von Verbindungen pro eindeutiger Verbindungszeichenfolge.
idle_timeout 600 Sekunden (10 Minuten) Anzahl der Sekunden, bevor die Leerlaufverbindungen geschlossen werden.
import mssql_python

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

Konfigurieren von Verbindungspooling

Konfigurieren Sie Pooling, bevor Sie Verbindungen herstellen:

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)

Parameter

Die pooling() Funktion akzeptiert die folgenden Parameter:

Parameter Typ Vorgabe Beschreibung
max_size int 100 Maximale Anzahl der gepoolten Verbindungen pro Verbindungszeichenfolge.
idle_timeout int 600 Sekunden, bevor inaktive Verbindungen aus dem Pool entfernt werden.
enabled bool True Pooling aktivieren oder deaktivieren.

Verbindungspooling deaktivieren

Um das Pooling zu deaktivieren, rufen Sie pooling() mit enabled=False auf, bevor Sie Verbindungen erstellen:

import mssql_python

mssql_python.pooling(enabled=False)

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

Note

Stellen Sie die Pooling-Konfiguration ein, bevor Sie irgendwelche Verbindungen herstellen. Der Aufruf von pooling() nach dem Aufbau von Verbindungen hat keine Auswirkung.

Wie Pooling funktioniert

Isolation von Verbindungsstrings

Jede eindeutige Verbindungszeichenfolge verfügt über einen eigenen unabhängigen Pool. Pools teilen keine Verbindungen über verschiedene Verbindungsstrings:

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

Verbindungslebenszyklus

Erfassen (eine Verbindung herstellen):

  1. Der Pool entfernt veraltete (im Leerlauf abgelaufene) Verbindungen.
  2. Der Pool versucht, eine bestehende Verbindung wiederzuverwenden:
    • Es prüft, ob die Verbindung aktiv ist.
    • Es setzt den Verbindungszustand zurück.
    • Wenn beide Prüfungen erfolgreich sind, wird die Verbindung zurückgegeben.
  3. Wenn keine wiederverwendbare Verbindung vorhanden ist und der Pool unter max_sizeliegt, erstellt der Treiber eine neue Verbindung.
  4. Wenn der Pool seine Kapazität erreicht hat und keine gültigen Verbindungen vorhanden sind, meldet der Treiber einen Fehler.

Release (Verbindung zurückgeben):

  1. Wenn der Pool Kapazität hat, speichert er die Verbindung zur Wiederverwendung.
  2. Wenn sich der Pool bei max_size befindet, schließt der Treiber die Verbindung sofort.

Verbindungs-Gesundheitsprüfungen

Der Treiber führt Überprüfungen des Verbindungszustands durch, bevor er eine Verbindung aus dem Pool wiederverwendet.

  1. Alive-Check: Stellt sicher, dass die Netzwerkverbindung noch gültig ist.
  2. Reset-Check: Setzt den Sitzungszustand (Isolationslevel, Einstellungen) für eine saubere Wiederverwendung zurück.

Wenn eine der Prüfungen fehlschlägt, verwirft der Pool die Verbindung und erstellt eine neue.

Automatische Reinigung

  • Leerlaufzeit: Der Treiber schließt Verbindungen, die länger als der Wert idle_timeout ungenutzt sind.
  • Prozess beenden: Ein Handler atexit schließt alle gepoolten Verbindungen, wenn der Python-Prozess endet.

Bewährte Methoden

Dimensionieren Sie Ihren Pool entsprechend

Passen Sie die Größe Ihres Pools an die Parallelität Ihrer Anwendung an.

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

Verwenden Sie Kontextmanager

Kontextmanager sorgen dafür, dass Sie die Verbindungen ordnungsgemäß zum Pool zurückgeben.

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

Halte die Verbindungsstrings konsistent

Verschiedene Parameter in Verbindungsstrings erzeugen separate Pools.

# 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

Betrachte Azure SQL-Verbindungsgrenzen

Azure SQL-Datenbank erzwingt Verbindungslimits basierend auf der Service-Schicht. Die folgenden Werte sind ungefähr; Überprüfen Sie die verlinkte Dokumentation für aktuelle Grenzwerte:

Dienstebene Maximal zulässige Anzahl paralleler Verbindungen
Basic 30
Standard S0-S2 60-120
Standard S3 und spätere Versionen 200
Premium 500

Dimensionieren Sie Ihren max_size-Wert unter diese Grenzwerte.

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

Idle-Timeout für Ihren Workload anpassen

  • Häufige Verbindungen: Verwenden Sie einen längeren idle_timeout Wert, um die Verbindungen warm zu halten.
  • Sporadische Verbindungen: Verwenden Sie einen kürzeren idle_timeout Wert, um Ressourcen freizusetzen.
# 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

Einschränkungen

Die aktuelle Implementierung hat einige Einschränkungen im Vergleich zu anderen Treibern:

Funktion Status
ClearPool() / ClearAllPools() Nicht verfügbar.
Poolstatistik/Überwachung Nicht verfügbar.
Pool-Übersteuerung pro Verbindung Nicht verfügbar.
Mindestpoolgröße Nicht konfigurierbar.

Beispiel: Webanwendungsmuster

Das folgende Flask-Beispiel zeigt, wie Verbindungen transparent über Anfragen hinweg gepoolt werden:

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

Erschöpfung des Pools erkennen

Wenn alle Verbindungen im Pool genutzt werden und Sie eine neue Verbindung anfordern, sehen Sie Symptome wie:

  • Kontakte hängen oder dauern aus, während man auf eine kostenlose Verbindung wartet.
  • Der Anwendungsdurchsatz sinkt plötzlich unter Last.
  • Der Speicherverbrauch steigt, wenn der Treiber Verbindungen herstellt, die er nicht wiederverwenden kann.

Häufige Ursachen:

  • Die Verbindungen werden nicht an den Pool zurückgegeben. Schließe immer die Verbindungen, wenn du fertig bist, oder nutze Kontextmanager. Eine Verbindung, die nicht geschlossen ist, bleibt überprüft.
  • Der Pool ist zu klein für die Arbeitsbelastung. Wenn du 50 gleichzeitige Anfragen hast, aber max_size=20, warten 30 Anfragen.
  • Lang andauernde Abfragen belegen Verbindungen. Teile lange Operationen auf oder nutze dedizierte Verbindungen für Batch-Arbeiten.

Problembehandlung:

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