Pulowanie połączeń z mssql-python

Pula połączeń poprawia wydajność aplikacji, ponownie używając połączeń z bazą zamiast tworzyć nowe połączenia dla każdego żądania. Otwarcie połączenia wymaga wielu czasochłonnych kroków:

  • Sterownik ustanawia gniazdo sieciowe.
  • Sterownik kończy utrzask dłoni TLS.
  • Sterownik uwierzytelnia się z serwerem.
  • Sterownik weryfikuje parametry połączenia.

Pula połączeń utrzymuje połączenia otwarte i dostępne do ponownego użycia, więc Twoja aplikacja nie musi powtarzać tych kroków dla każdego żądania.

Domyślne zachowanie

Buforowanie połączeń jest domyślnie włączone podczas tworzenia pierwszego połączenia. Ustawienia domyślne to:

Setting Wartość domyślna Opis
max_size 100 Maksymalna liczba połączeń na unikalny parametry połączenia.
idle_timeout 600 sekund (10 minut) Liczba sekund przed zamknięciem bezczynnych połączeń.
import mssql_python

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

Konfigurowanie puli połączeń

Skonfiguruj pulowanie połączeń przed utworzeniem jakichkolwiek połączeń:

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

Funkcja pooling() akceptuje następujące parametry:

Parameter Typ Domyślnie Opis
max_size int 100 Maksymalna liczba połączonych połączeń w puli na jeden parametry połączenia.
idle_timeout int 600 Liczba sekund, po upływie których bezczynne połączenia są usuwane z puli.
enabled bool True Włącz lub wyłącz pulowanie.

Wyłącz pulowanie połączeń

Aby wyłączyć pulowanie, wywołaj pooling() za pomocą enabled=False przed utworzeniem połączeń:

import mssql_python

mssql_python.pooling(enabled=False)

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

Note

Ustaw konfigurację puli przed nawiązaniem jakichkolwiek połączeń. Dzwonienie pooling() po nawiązaniu połączeń nie ma żadnego wpływu.

Jak działa pooling

Izolacja ciągu połączeń

Każdy unikalny zestaw parametrów połączenia ma własną, niezależną pulę. Pule nie współużytkują połączeń między różnymi łańcuchami połączeń:

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

Cykl życia połączenia

Uzyskiwanie (nawiązywanie połączenia):

  1. Pula usuwa przestarzałe (bezczynne) połączenia.
  2. Pula próbuje ponownie wykorzystać istniejące połączenie:
    • Sprawdza, czy połączenie jest aktywne.
    • Resetuje stan połączenia.
    • Jeśli oba testy zakończą się sukcesem, połączenie przywraca się.
  3. Jeśli nie ma dostępnego połączenia do ponownego użycia, a liczba połączeń w puli jest mniejsza niż max_size, sterownik tworzy nowe połączenie.
  4. Jeśli pula osiągnęła maksymalną pojemność i nie ma żadnych prawidłowych połączeń, sterownik zgłasza błąd.

Zwolnienie (zwrócenie połączenia):

  1. Jeśli pula ma pojemność, przechowuje połączenie do ponownego wykorzystania.
  2. Jeśli pula połączeń jest na poziomie max_size, sterownik natychmiast zamyka połączenie.

Sprawdzenie stanu połączenia

Sterownik przeprowadza sprawdzenie stanu połączenia przed ponownym użyciem połączenia w grupie.

  1. Alive check: Sprawdza, czy połączenie sieciowe jest nadal ważne.
  2. Reset check: Resetuje stan sesji (poziom izolacji, ustawienia) dla czystego ponownego użycia.

Jeśli którekolwiek ze sprawdzeń zakończy się niepowodzeniem, pula odrzuca połączenie i tworzy nowe.

Automatyczne oczyszczanie

  • Limit czasu bezczynności: Sterownik zamyka połączenia, które nie są używane dłużej niż wynosi wartość idle_timeout.
  • Wyjście z procesu: Handler atexit zamyka wszystkie połączenia w grupie, gdy proces Python się kończy.

Najlepsze rozwiązania

Odpowiednio dopasuj swój basen

Dopasuj wielkość puli do poziomu współbieżności aplikacji.

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

Użyj menedżerów kontekstu

Menedżerowie kontekstu dbają o prawidłowe przywracanie połączeń do puli.

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

Utrzymuj spójność ciągów połączeń

Różne parametry w łańcuchach połączeń tworzą oddzielne pule.

# 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

Rozważ ograniczenia połączeń Azure SQL

Azure SQL Database egzekwuje limity połączeń w zależności od poziomu usług. Następujące wartości są przybliżone; Sprawdź powiązaną dokumentację dotyczącą aktualnych limitów:

Poziom usługi maksymalna liczba współbieżnych połączeń
Basic 30
Standard S0-S2 60-120
Standardowe wersje S3 i późniejsze 200
Premium 500

Ustaw rozmiar wartości max_size poniżej poniższych limitów.

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

Dostrojcie czas na bezczynność dla swojego obciążenia pracą

  • Częste połączenia: Użyj dłuższej wartości idle_timeout, aby utrzymać połączenia aktywne.
  • Połączenia sporadyczne: Użyj krótszej wartości idle_timeout, aby zwolnić zasoby.
# 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

Obecna implementacja ma pewne ograniczenia w porównaniu do innych sterowników:

Funkcja Status
ClearPool() / ClearAllPools() Niedostępne.
Statystyki i monitorowanie puli Niedostępne.
Nadpisywanie puli per-połączeń Niedostępne.
Minimalny rozmiar puli Nie można skonfigurować.

Przykład: Wzorzec aplikacji webowej

Poniższy przykład Flask pokazuje, jak połączenia są przezroczysto pulowane między żądaniami:

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

Rozpoznaj wyczerpanie basenu

Gdy wszystkie połączenia w puli są zajęte i próbujesz uzyskać nowe połączenie, występują następujące symptomy:

  • Połączenia zawieszają się lub przepadają podczas oczekiwania na wolne połączenie.
  • Przepustowość aplikacji nagle spada pod obciążeniem.
  • Zużycie pamięci rośnie, gdy sterownik tworzy połączenia, których nie może ponownie wykorzystać.

Typowe przyczyny:

  • Połączenia nie wracają do puli. Zawsze zamykaj kontakty, gdy skończysz, albo używaj menedżerów kontekstu. Połączenie, które nie jest zamknięte, pozostaje w zasięgu kontroli.
  • Zasób jest zbyt mały dla tego obciążenia. Jeśli masz 50 jednoczesnych żądań, ale max_size=20, 30 żądań czeka.
  • Długotrwałe zapytania utrzymują powiązania. Podziel długie operacje lub użyj dedykowanych połączeń do pracy wsadowej.

Jak to naprawić:

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