高流量網站(日 PV 500 萬、獨立 IP 3 萬)在 Windows 2003 + ASP.NET + SQL Server 2000 環境下,出現詭異現象:記憶體穩定 218MB,CPU 閒置僅 5%,但**每 20 秒固定飆升至 30%-50%**。重新部署後前 30 分鐘正常,之後開始周期性飆升。本文記錄完整排查過程,揭示 `Session_End` 事件在大量 Session 過期清理時的效能殺手本質。
void Session_End(object sender, EventArgs e) {
if (Session.SessionID != "") {
Application.Remove("id" + Session.SessionID);
Application.Contents.Remove("id" + Session.SessionID);
}
}
註解掉此段程式碼後重新部署,**CPU 立即恢復平穩**,確認禍首。
void Session_End(object sender, EventArgs e) {
if (Session.SessionID != "") {
string sessionId = Session.SessionID;
string appContent = Application.Contents["id" + Session.SessionID].ToString();
SessionLog.Insert(sessionId, appContent, DateTime.Now); // 寫入資料庫記錄
Application.Remove("id" + Session.SessionID);
Application.Contents.Remove("id" + Session.SessionID);
}
}
半小時後查詢資料庫,發現觸發時間高度集中在 **00-01秒、20-21秒、40-41秒** 等區間,每次 **70-120 條記錄**,完美吻合 20 秒週期。
| 方案 | 優點 | 缺點/注意 |
|---|---|---|
| 移除 Session_End 邏輯 | 最徹底、零成本 | 失去 Session 結束時的清理機會 |
| 改用 StateServer / SQLServer Session 模式 | 不觸發 Session_End、支援 Web Farm | 序列化開銷、需額外服務 |
| 非同步批次清理 | 不阻塞請求執行緒 | 實作複雜、需佇列機制 |
| 縮減 Session Timeout | 減少同時存活 Session 數 | 影響用戶體驗 |
| Application 改用 ConcurrentDictionary | 無鎖高效能 | .NET 4.0+ 才支援 |
此案例為經典 ASP.NET 效能調優教材,提醒開發者重新審視 `Global.asax` 中看似無害的清理邏輯,在高併發場景下可能成為隱形殺手。