使用 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() 函數接受以下參數:

參數 類型 預設值 描述
max_size int 100 每個連線字串的最大集區連線數。
idle_timeout int 600 閒置連線從連線池中移出前的秒數。
enabled bool 沒錯 啟用或關閉池化。

停用連線集區

要停用池化,請在建立連線前先呼叫 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 值的連線。
  • 程序退出:當 Python 程序退出時,atexit處理器會關閉所有合併的連線。

最佳做法

適當地調整你的池數

將你的池大小與應用程式的並行性相匹配。

# 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 會根據服務層級強制執行連線限制。 以下數值為近似值;請查看連結文件中的電流限制:

服務層級 並行連線數上限
基本 30
標準 S0-S2 60-120
標準 S3 及後續版本 200
進階 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

局限性

目前的實作與其他驅動程式相比有一些限制:

Feature 狀態
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)