Пул соединений с mssql-python

Пул соединений повышает производительность приложений, используя повторное использование соединений с базами данных вместо создания новых для каждого запроса. Открытие соединения требует нескольких трудоёмких этапов:

  • Драйвер устанавливает сетевой сокет.
  • Драйвер завершает рукопожатие TLS.
  • Драйвер аутентифицируется с сервером.
  • Драйвер проверяет параметры соединения.

Пул соединений оставляет соединения открытыми и доступными для повторного использования, поэтому вашему приложению не нужно повторять эти шаги для каждого запроса.

Поведение по умолчанию

Пул соединений включен по умолчанию при создании первого соединения. Параметры по умолчанию:

Setting Значение по умолчанию Описание
max_size 100 Максимальное количество соединений для уникальной строки подключения.
idle_timeout 600 секунд (10 минут) Количество секунд до закрытия холостых соединений.
import mssql_python

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

Настройка пула подключений

Настройте пул соединений перед созданием любых соединений:

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

Функция pooling() принимает следующие параметры:

Parameter Type По умолчанию Описание
max_size int 100 Максимальное количество соединений в пуле для каждой строки подключения.
idle_timeout int 600 Количество секунд, по истечении которых неактивные соединения удаляются из пула.
enabled bool True Включите или отключите объединение.

Отключить пул подключений

Чтобы отключить пул соединений, вызовите pooling() с enabled=False перед созданием соединений:

import mssql_python

mssql_python.pooling(enabled=False)

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

Замечание

Задайте конфигурацию пула перед установкой любых соединений. Вызов pooling() после создания соединений не имеет эффекта.

Как работает объединение ресурсов

Изоляция соединительных строк

Каждая уникальная строка подключения имеет собственный независимый пул. Пулы не используют общие соединения для разных строк подключения:

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

Жизненный цикл соединения

Установление (получение соединения):

  1. Пул удаляет соединения с истекшим временем простоя.
  2. Пул пытается повторно использовать существующее соединение:
    • Проверяет, активно ли соединение.
    • Сбрасывает состояние соединения.
    • Если обе проверки успешны, соединение возвращается.
  3. Если нет доступного для повторного использования соединения и размер пула меньше max_size, драйвер создаёт новое соединение.
  4. Если пул полностью загружен и нет корректных соединений, драйвер выдает ошибку.

Релиз (восстановление соединения):

  1. Если в пуле есть место, соединение сохраняется для повторного использования.
  2. Если пул находится в состоянии max_size, драйвер немедленно закрывает соединение.

Проверки состояния соединения

Драйвер выполняет проверку состояния соединения перед повторным использованием пулового соединения.

  1. Проверка активности: Проверяет, что сетевое соединение по-прежнему активно.
  2. Сброс: Сбрасывает состояние сеанса (уровень изоляции, настройки) для последующего повторного использования.

Если одна из проверок не работает, пул отбрасывает соединение и создаёт новое.

Автоматическая очистка

  • Тайм-аут на холостом ходу: драйвер закрывает соединения, которые не используются дольше значения idle_timeout .
  • Выход из процесса: atexit Обработчик закрывает все объединённые соединения при завершении процесса Python.

Лучшие практики

Подобрайте подходящий размер бассейна

Подберите размер пула в соответствии с уровнем параллелизма вашего приложения.

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

Используйте менеджеры контекста

Менеджеры контекста гарантируют корректный возврат соединений в пул.

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

Поддерживайте последовательные строки соединения

Разные параметры в строках соединения создают отдельные пулы.

# 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

Рассмотрите ограничения соединения Azure SQL

База данных SQL Azure обеспечивает ограничения соединения в зависимости от уровня сервиса. Следующие значения являются приблизительными; Проверьте связанные документы для актуальных лимитов:

Уровень служб Максимальное число одновременных подключений
Basic 30
Стандарт S0-S2 60-120
Стандартные версии S3 и более поздние 200
Premium 500

Укажите значение max_size в пределах, указанных ниже.

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

Настройте тайм-аут в режиме ожидания под свою нагрузку

  • При частых соединениях: используйте большее значение idle_timeout, чтобы соединения оставались активными.
  • Нерегулярные подключения: используйте меньшее значение idle_timeout, чтобы освободить ресурсы.
# 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

Текущая реализация имеет некоторые ограничения по сравнению с другими драйверами:

Функция Status
ClearPool() / ClearAllPools() Недоступно.
Статистика/мониторинг пула Недоступно.
Переопределение пула для каждого соединения Недоступно.
Минимальный размер бассейна Не настраиваемая.

Пример: шаблон веб-приложения

Следующий пример Flask показывает, как соединения прозрачно объединяются между запросами:

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

Распознайте истощение бассейна

Когда все соединения в пуле используются и вы запрашиваете новое соединение, вы можете наблюдать следующие симптомы:

  • Соединения зависают или перерываются, пока ждут бесплатного соединения.
  • Пропускная способность приложений внезапно падает под нагрузкой.
  • Использование памяти растёт, когда драйвер создаёт соединения, которые не может использовать повторно.

Распространенные причины:

  • Соединения не возвращаются в пул. Всегда закрывайте контакты после завершения или используйте менеджеры контекста. Если соединение не закрыто, оно остаётся занятым.
  • Размер бассейна слишком мал для нагрузки. Если у вас 50 одновременных запросов, но max_size=20, 30 запросов будут ожидать.
  • Длительные запросы удерживают соединения. Разбивайте длительные операции или используйте выделенные соединения для пакетной работы.

Как исправить:

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