Agrupación de conexiones con mssql-python

El pooling de conexiones mejora el rendimiento de las aplicaciones al reutilizar conexiones a bases de datos en lugar de crear nuevas para cada solicitud. Abrir una conexión implica varios pasos que consumen mucho tiempo:

  • El controlador establece un conector de red.
  • El controlador completa el handshake TLS.
  • El controlador se autentica con el servidor.
  • El controlador valida los parámetros de conexión.

El pooling de conexiones mantiene las conexiones abiertas y disponibles para su reutilización, así que tu aplicación no necesita repetir estos pasos para cada solicitud.

Comportamiento predeterminado

El pooling de conexiones está activado por defecto cuando creas tu primera conexión. La configuración predeterminada es la siguiente:

Configuración Valor predeterminado Descripción
max_size 100 Número máximo de conexiones por cada cadena de conexión única.
idle_timeout 600 segundos (10 minutos) Número de segundos antes de que se cierren las conexiones en reposo.
import mssql_python

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

Configuración de la agrupación de conexiones

Configura el pooling antes de crear cualquier conexión:

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() función acepta los parámetros siguientes:

Parámetro Tipo Default Descripción
max_size int 100 Número máximo de conexiones agrupadas por cadena de conexión.
idle_timeout int 600 Segundos antes de que las conexiones inactivas sean desalojadas de la piscina.
enabled bool True Activa o desactiva el pooling.

Desactivar la agrupación de conexiones

Para desactivar la agrupación, llama a pooling() con enabled=False antes de crear conexiones:

import mssql_python

mssql_python.pooling(enabled=False)

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

Note

Configura la configuración de pooling antes de establecer cualquier conexión. Llamar pooling() después de crear conexiones no tiene ningún efecto.

Cómo funciona la agrupación

Aislamiento de cadenas de conexión

Cada cadena de conexión distinta mantiene su propio grupo independiente. Los pools no comparten conexiones entre diferentes cadenas de conexión:

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

Ciclo de vida de la conexión

Conseguir (conseguir una conexión):

  1. La piscina elimina conexiones ranciadas (en reposo o expiradas).
  2. La piscina intenta reutilizar una conexión existente:
    • Comprueba si la conexión está activa.
    • Reinicia el estado de conexión.
    • Si ambas comprobaciones tienen éxito, devuelve la conexión.
  3. Si no existe ninguna conexión reutilizable y el pool está bajo max_size, el controlador crea una nueva conexión.
  4. Si el pool está a plena capacidad sin conexiones válidas, el controlador genera un error.

Liberar (devolver una conexión):

  1. Si la piscina tiene capacidad, almacena la conexión para su reutilización.
  2. Si el pool está en max_size, el driver cierra la conexión inmediatamente.

Chequeos de salud de la conexión

El controlador realiza comprobaciones de salud de la conexión antes de reutilizar una conexión agrupada.

  1. Comprobación de actividad: Garantiza que la conexión de red siga siendo válida.
  2. Comprobación de reinicio: Reinicia el estado de la sesión (nivel de aislamiento, ajustes) para una reutilización limpia.

Si alguna de las pruebas falla, el pool descarta la conexión y crea una nueva.

Limpieza automática

  • Tiempo de espera en reposo: El controlador cierra conexiones que no se usan durante más tiempo que el idle_timeout valor.
  • Salida del proceso: Un atexit gestor cierra todas las conexiones agrupadas cuando el proceso en Python sale.

procedimientos recomendados

Dimensiona tu piscina adecuadamente

Ajusta el tamaño de tu grupo a la concurrencia de tu solicitud.

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

Utilizar gestores de contexto

Los gestores de contexto se aseguran de que devuelvas correctamente las conexiones 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

Mantén las cadenas de conexión consistentes

Diferentes parámetros en las cadenas de conexión crean 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

Consideremos los límites de conexión de Azure SQL

Azure SQL Database aplica límites de conexión basados en el nivel de servicio. Los siguientes valores son aproximados; Consulta la documentación enlazada para conocer los límites actuales:

Nivel de servicio número máximo de conexiones simultáneas
Basic 30
S0-S2 estándar 60-120
Versiones estándar S3 y posteriores 200
Premium 500

Ajusta el valor de max_size para que quede por debajo de estos límites.

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

Ajusta el tiempo de espera de inactividad para tu carga de trabajo

  • Conexiones frecuentes: Usa un valor más largo idle_timeout para mantener las conexiones calientes.
  • Conexiones esporádicas: Usa un valor más idle_timeout corto 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

La implementación actual tiene algunas limitaciones en comparación con otros controladores:

Feature Situación
ClearPool() / ClearAllPools() No está disponible.
Estadísticas/supervisión de grupos No está disponible.
Sobrescritura del pool por conexión No está disponible.
Tamaño mínimo de la piscina No se puede configurar.

Ejemplo: patrón de aplicación web

El siguiente ejemplo de Flask muestra cómo las conexiones se agrupan de forma transparente entre peticiones:

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

Reconoce el agotamiento en la piscina

Cuando todas las conexiones de la piscina están en uso y solicitas una nueva conexión, observas síntomas como:

  • Las conexiones se quedan o se apagan mientras esperan una conexión libre.
  • El rendimiento de la aplicación cae repentinamente bajo carga.
  • El consumo de memoria aumenta a medida que el controlador crea conexiones que no puede reutilizar.

Causas comunes:

  • Las conexiones no se devuelven a la piscina. Siempre cierra las conexiones cuando termines, o usa gestores de contexto. Una conexión que no está cerrada permanece comprobada.
  • La piscina es demasiado pequeña para la carga de trabajo. Si tienes 50 solicitudes concurrentes pero max_size=20tienes 30 solicitudes en espera.
  • Las consultas de larga duración mantienen conexiones. Divide las operaciones de larga duración o usa conexiones dedicadas para el procesamiento por lotes.

Cómo corregir:

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