2008/08/18
SqlException(0x80131904):已超過連接逾時的設定
昨天幫同事看一個問題,User反應網頁查詢某報表都會發生「SqlException(0x80131904):已超過連接逾時的設定」(問題一),另外即使縮小查詢範圍到只有數百筆仍然會發生相同錯誤,但是過去不會發生(問題二)。他初步懷疑是SQL Server查詢的回應時間Timeout所致,網路上討論的文章也有人提供類似假設,都是建議開放更多的連線時間、connection pooling,這真是可怕的建議。 另外少數有良心的程式設計師的說法我就很讚同:「程式遇到問題不應該總是向伺服器索取更多資源,應該回頭檢視程式的邏輯是否合理?是否用錯演算法或查詢指令造成系統資源的浪費。」 用這樣的原則分析同事拋出的兩個問題,問題二「縮小範圍到只有數百筆仍會發生相同錯誤」這才是問題的核心,明顯是查詢指令有問題,最常發生都是JOIN TABLE的關係錯置所致。過去經驗,改善查詢指令後程式效能往往能提升30%~70%,這比任何硬體升級都能解決網站效能。不過大部分的IT主管似乎比較迷信硬體升級,以為花錢買設備解決問題最快,結果設備買了不到一個月問題照常發生,也只能用發生頻率變低安慰自己。 優化查詢指令後,如果仍發生「SqlException(0x80131904):已超過連接逾時的設定」。可以計算從aspx網頁下submit後response的時間。 System.Data.SqlClient 的connection timeout時間預設為30秒,網路上很多村民建議在web.config設定延長此時間,這是萬萬不可啊!預設的30秒的查詢回應足以應付大部分的需求。不得已向伺服器索取更多資源的「特殊需求」,應該只限於單一查詢指令。 於是我在程式碼中作了如下修改: Dim sqlConnection1 As New SqlConnection(connString & "Connect Timeout=60") ...... cmd.CommandTimeout=60 ...... reader.Close() 將connection及command Timeout時間都延長到60秒,增加一倍的時間已經很多,「前人」忘了釋放DataReader的資源看到後當然要順手處理。經此調校後同事的系統已經正常運作。 Debug的技術面不難,但是處理問題的「原則」跟「態度」會決定這個系統是否走向「暴斃」或延續。 完整的錯誤訊息如下: '/aspx' 應用程式中發生伺服器錯誤。 -------------------------------------------------------------------------------- 已超過連接逾時的設定。在作業完成之前超過逾時等待的時間,或者是伺服器未回應。 描述: 在執行目前 Web 要求的過程中發生未處理的例外情形。請檢閱堆疊追蹤以取得錯誤的詳細資訊,以及在程式碼中產生的位置。 例外詳細資訊: System.Data.SqlClient.SqlException: 已超過連接逾時的設定。在作業完成之前超過逾時等待的時間,或者是伺服器未回應。 ...(略)... 堆疊追蹤: [SqlException (0x80131904): 已超過連接逾時的設定。在作業完成之前超過逾時等待的時間,或者是伺服器未回應。] System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +857146 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +734758 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +188 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +1838 System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +31 System.Data.SqlClient.SqlDataReader.get_MetaData() +62 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +297 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +886 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +132 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32 System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +122 System.Data.SqlClient.SqlCommand.ExecuteReader() +84 ...(略)