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()関数は、次のパラメーターを受け入れます。

パラメーター タイプ Default 説明
max_size int 100 1つの接続文字列あたりの最大プーリング接続数。
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)

Note

接続を確立する前にプーリング設定を設定してください。 接続を作成した後に 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 接続制限

Azure SQL Databaseはサービス階層に基づいて接続制限を強制します。 以下の値はおおよそのものです。電流制限についてはリンク先のドキュメントをご覧ください:

サービス階層 最大同時接続数
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

現在の実装には他のドライバーと比べていくつかの制限があります:

特徴 地位
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)