前言
身為後端工程師,你是否遇過這樣的情況:
- 系統突然變得超級慢
- 網頁一直轉圈圈
- 收到「已超過連線逾時的設定」錯誤訊息
如果有,很可能就是連線池爆掉了。以下分享一個真實案例,從診斷、緊急處理到根治。
問題診斷
當你遇到以下錯誤訊息時,很可能是連線池出問題了:
1 | System.InvalidOperationException: '已超過連線逾時的設定。在取得集區連線之前超過逾時等待的時間,可能的原因為所有的共用連線已在使用中,並已達共用集區大小的最大值。' |
快速診斷方法
首先,用以下 SQL 查看目前的連線狀況。sys.sysprocesses 是 SQL Server 2000 相容性視圖,SQL Server 2005 起已標記 deprecated,這裡改用官方建議的 DMV:
1 | -- 各資料庫連線數摘要(依登入帳號、狀態分組) |
緊急處理方案
發現問題後,可以使用以下步驟處理:
- 檢視問題連線
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61-- 1. 查看該資料庫的連線詳情(SQL Server 2005+ DMV)
SELECT
s.session_id AS '處理序 ID',
DB_NAME(s.database_id) AS '資料庫',
s.login_name AS '登入名稱',
s.host_name AS '主機名',
s.program_name AS '程式名稱',
s.login_time AS '登入時間',
s.last_request_start_time AS '最後執行時間',
s.status AS '狀態'
FROM sys.dm_exec_sessions AS s
WHERE s.database_id = DB_ID('資料庫名稱') -- 替換成你的資料庫名
AND s.is_user_process = 1
ORDER BY s.login_time DESC;
-- 2. 查看具體在執行什麼 SQL
SELECT
s.session_id,
r.blocking_session_id AS '被阻塞源',
s.host_name AS '主機名',
s.program_name AS '程式名稱',
r.command AS '指令',
r.cpu_time AS 'CPU時間',
r.reads + r.writes AS '實體IO',
r.start_time AS '開始執行時間',
t.[text] AS 'SQL內容'
FROM sys.dm_exec_sessions AS s
JOIN sys.dm_exec_requests AS r ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
WHERE s.database_id = DB_ID('資料庫名稱') -- 替換成你的資料庫名
AND s.is_user_process = 1;
-- 3. 殺掉特定連線(請小心使用)
-- KILL 52; -- 替換成你要關閉的 session_id
-- 4. 如果確定要關閉所有使用者連線,可以使用動態 SQL(需要特別小心)
-- SQL Server 保留 session_id ≤ 50 給系統內部進程,所以只 KILL > 50 的
DECLARE @kill varchar(8000) = '';
SELECT @kill = @kill + 'KILL ' + CONVERT(varchar(5), s.session_id) + ';'
FROM sys.dm_exec_sessions AS s
WHERE s.database_id = DB_ID('資料庫名稱') -- 替換成你的資料庫名
AND s.is_user_process = 1;
-- 執行前先確認要關閉的連線
PRINT @kill;
-- 如果確認無誤,才可以執行(請小心使用)
-- EXEC(@kill);
-- 5. ⚠️ 高風險操作:切換到單一用戶模式
-- 警告:這不是連線池爆掉的正確解法。
-- SET SINGLE_USER 會立即 rollback 所有進行中的交易、強制踢掉全部連線,
-- 造成整個資料庫暫時無法服務(生產環境等同短暫停機)。
-- 僅適用於資料庫維護(如 DBCC CHECKDB)等明確需要獨占存取的場景,
-- 連線池問題應從應用端未釋放連線的根因下手,而非用此指令強制清場。
ALTER DATABASE 資料庫名稱
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
-- 完成後務必切換回多用戶模式,否則所有應用連線將持續被拒絕
-- ALTER DATABASE 資料庫名稱 SET MULTI_USER;
程式碼優化建議
這次的根因是前人寫的 VB 程式碼沒有正確釋放資料庫連線。以下是修正方向:
使用 Using 區塊確保資源釋放
1
2
3Using connection As New SqlConnection(connectionString)
' 資料庫操作
End Using避免全域宣告資料庫連線
1
2
3
4
5
6
7
8
9' 不建議
Dim conn As New SqlConnection '全域變數
' 建議
Private Function GetData() As DataTable
Using conn As New SqlConnection(connectionString)
' 程式碼
End Using
End Function設定適當的連線池參數
1
2
3
4
5
6
7<connectionStrings>
<add name="MyConnection"
connectionString="Server=myserver;Database=mydb;
Max Pool Size=100;Min Pool Size=5;
Connection Timeout=30;"
providerName="System.Data.SqlClient" />
</connectionStrings>
Max Pool Size 預設值就是 100,直接填 100 等於沒改。實際調整依據:若應用有高峰期 N 個 thread 同時存取資料庫,Pool Size 設成接近 N;設太大反而讓 SQL Server 同時承受過多連線,效能反降。調整前先用上面的 DMV 查看實際峰值再決定。
注意:
System.Data.SqlClient是 .NET Framework 內建的舊版 provider,已進入維護模式。新專案或 .NET Core/.NET 5+ 環境,建議改用 NuGet 套件Microsoft.Data.SqlClient,微軟已將後者列為主動開發的標準驅動。舊系統維護沿用舊 provider 沒問題,但新專案不建議照搬這段連線字串。
預防措施
監控:SQL Server Agent 可設定 Job 定期執行上方的 DMV 查詢,連線數超過門檻(例如 > 80)就寄信或寫事件日誌。門檻值參考 Max Pool Size,建議設在 80% 左右就開始警示,留緩衝時間處理。
程式碼審查重點:SqlConnection 必須在 Using 區塊或 try/finally 中確保 Dispose()。容易漏的地方是早期 VB 程式的全域連線變數、以及 DataAdapter.Fill() 後沒關連線的寫法。
重試機制:瞬間高峰時連線池滿是短暫現象,可在取連線失敗時加入指數退避重試(最多 3 次,每次間隔 200–500ms);持續滿才代表有連線洩漏。




