鏈接池和 "Timeout expired"異常【轉】

異常信息:mysql

MySql.Data.MySqlClient.MySqlException (0x80004005): error connecting: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
at MySql.Data.MySqlClient.MySqlPool.GetConnection()
at MySql.Data.MySqlClient.MySqlConnection.Open()
at ArticleSys.MySqlHelper.PrepareCommand(MySqlCommand cmd, MySqlConnection conn, MySqlTransaction trans, CommandType cmdType, String cmdText, MySqlParameter[] cmdParms)
at ArticleSys.MySqlHelper.GetDataTable(String mysqlConnStr, CommandType cmdType, String cmdText, MySqlParameter[] commandParameters)
=====================2013-10-10 12:28:33==============sql

------------如下爲正文數據庫

System.InvalidOperationException: Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.   This may have occurred because all pooled connections were in use and max pool size was reached.性能


Timeout expired 異常是個很棘手的異常,想必幾乎每一個人都碰到過。有時可真是對它咬牙切齒,拿它沒辦法。 angelsb這篇文章很好,但願對你們有用。我也是看到他講得很好,才翻譯過來的,水平有限,請多多指教.


System.InvalidOperationException: Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.   This may have occurred because all pooled connections were in use and max pool size was reached.

哎!在另外一個進程中,又出現了鏈接池已滿的問題,這是個最讓人頭痛卻又是最常出現的鏈接池問題之一.緣由是在開發過程當中不多碰到這個頭痛的問題,但在部署APP到客戶端時,卻老是不經意地跑出來了.我想,我應該花些許時間對這個問題進行一次完整的總結吧.

發生的本質是什麼?

咱們來認真看一下可能會發生這種異常的兩種狀況

1) 你使用了超過最大的鏈接池鏈接數(默認的最大鏈接數是100)

在大部分應用程序中,這種狀況是不多出現的. 畢竟當你使用鏈接池時,100個並行鏈接是一個很是大的數字.根據個人經驗,會形成這種異常的緣由的最大可能,應該是在一個純種下打開了100個鏈接.

this



SqlConnection[] connectionArray = new SqlConnection[101];
    for (int i = 0; i <= 100; i++)
    {
        connectionArray[i] = new SqlConnection("Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5");
        connectionArray[i].Open();
    }




解決方案:若是你肯定你將會使用超過100個並行鏈接(在同一鏈接字符串上),你能夠增長最大鏈接數.

2) 鏈接泄漏

我我的認爲的鏈接泄漏定義是你打開了一個鏈接但你沒有在你的代碼中執行close()或dispose().這範圍不只僅是你忘記了在connection後鏈接後使用dispose()或close()對期進行關閉,還包括一些你已經在相關connection後寫好了close()卻根本沒有起做用的情況.咱們來看看下面的代碼:

spa



using System;
using System.Data;
using System.Data.SqlClient;

public class Repro
{
    public static int Main(string[] args)
    {
        Repro repro = new Repro();
        for (int i = 0; i <= 5000; i++)
        {
            try{ Console.Write(i+" ");    repro.LeakConnections(); }
            catch (SqlException){}
        }

        return 1;
    }
    public void LeakConnections()
    {    
        SqlConnection sqlconnection1 = new SqlConnection("Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5");
        sqlconnection1.Open();
        SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
        sqlcommand1.CommandText = "raiserror ('This is a fake exception', 17,1)";
        sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
        sqlconnection1.Close(); //We are calling connection close, and we are still leaking connections (see above comment for explanation)
    }
}




這就是一個典型的例子,將這段代碼複製到visual Studio中,在 sqlconnection1.close()中設置一個斷點,編譯時能夠看到他永遠沒有執行,由於ExecuteNonQurery拋出了一個異常.以後你應該能夠看到恐怖的超時異常了. 在個人機子上,大約有170個鏈接被打開. 我曾想讓其在每次調用的時候將異常拋出來達到下降鏈接超時出現的機率,但當你考慮到將其部署到一個ASP.NET的應用程序的時候,任何一個泄漏都將讓你處於麻煩之中.

3)你是經過visual Studio中的sql debugging 來打開或關閉鏈接的

這是一個衆所周知的Bug,能夠看一下下面這個連接
http://support.microsoft.com/default.aspx?scid=kb;en-us;830118



如何在ADO.NET2.0中判斷是不是鏈接泄漏

在1.0或1.1中,咱們很難去判斷是不是鏈接泄漏,至多能夠經過一些性能指標或諸如此類的工做去實現.但在ADO.NET2.0中,若是你注意到NumberOfReclaimedConnections這個玩藝兒,就能夠知道你的應用程序是不是鏈接泄漏了.


時刻注意修復相關的鏈接字符串

修改相關的鏈接字符串可讓你暫時翻譯」逃過」一些異常,這是很是誘人的.特別是在一個高性能消耗時,修改它就顯示更爲必要了.


這裏是一些讓你的應用程序能」運行良好」的非正常行爲(搬起石頭砸本身的腳)

不要把Poooling=False

坦白的說,若是你將pooling設爲關閉狀態,你固然不會再碰到超時異常,可怕的是你的應用程序性能將大大下降,而你的鏈接仍然處於泄漏狀態.

不要把Connection LifeTime=1

這不是一個能清除異常的方法,但它多是最接近的一個解決方法.你想告訴咱們的是將全部的鏈接超過一秒鐘的鏈接都統統拋棄(正常的生命週期結束應該是在connetcio.close()後).我我的認爲這種方法上關閉鏈接池沒什麼兩樣.除非你是在使用數據庫的集羣,不然你不該設置鏈接週期來達到目的.


不要將 Connection TimeOut=40000


很是愚蠢的選擇,你這是在告訴咱們在拋出一個超時異常以前,你在無限地等待一個鏈接轉變爲可用的.幸好在ASP.NET中將會在三分鐘以後取消一個進程.


不要將Max PoolSize=4000;
若是你將鏈接池的最大數設置到足夠大的時候,你最終會將這異常中止.但在另外一方面,你將佔用了你的應用程序中才是真正須要的巨大的鏈接資源,這種作法只能飲鳩止渴.



解決方案:

你須要保證你每次調用鏈接的同時都在使用事後經過close()或dispose()對其執行了關閉.最簡單的辦法就是使用using,將你的鏈接泄漏方法修改爲以下面的代碼樣式:

public void DoesNotLeakConnections()
    {    
        Using (SqlConnection sqlconnection1 = new SqlConnection("Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5")) {
            sqlconnection1.Open();
            SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
            sqlcommand1.CommandText = "raiserror ('This is a fake exception', 17,1)";
            sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
            sqlconnection1.Close(); //Still never gets called.
                  } // Here sqlconnection1.Dispose is _guaranteed_
    }



FAQ:

Q:爲何要這樣作

A:使用using結構等同於Try/…/Finally{ .Dispose() ) 即便當ExecuteNonQuery會拋出一個執行錯誤時,咱們均可以保證finally模塊將會執行



Q:我上面的代碼中若是沒有異常拋出的話,我可使用close()或dispose()嗎

A:咱們毫無顧忌地用他們中的任意一個,或兩個同時使用.在一個已經close或dipose()的鏈接中使用close()或dispose()是不會影響的

Q:Close()和Dispose()有什麼不一樣,我應該用哪個好?
A:它們作的是同一件事,你能夠調用他們中的任意一個,或兩個同時使用.

Q:你所說的"practically the same thing」是什麼意思?
A:Dispose()將會經過sqlConnection來清理相關的鏈接,以後執行close().它們沒有什麼本質的區別,你能夠經過reflector來證實這點

Q:與close()相比,connection.dispose()會將鏈接些移除嗎?
A:不會

---------------------------------------------------------------

個人分享:

針對"Timeout expired"這個異常,我也查閱了不少資料。在國內咱們不少project都會採用MS提供的sqlhelper這個封裝類。由於這個類中有自己的缺陷所致,因此出現的"Timeout expiered"異常機率大。我在國外的一篇文章中看到的解決方案是:

將SqlHelper中的cmd.CommandTimeout="你要設置的秒數"加上去,從新編譯.



if (trans != null)
cmd.Transaction = trans;

cmd.CommandType = cmdType;
cmd.CommandTimeout = 240;


經過搜索,我找到了相關的一個代碼.
這個代碼是摘自國人的某位同行的,感謝
http://blog.csdn.net/long2006sky/archive/2007/07/09/1683459.aspx

eg:

**////
    /// 執行查詢語句,返回DataTable
    ///
    /// 查詢語句
    /// 設置查詢Timeout
    /// 用於複雜查詢
    public static DataTable GetDataTable(string SQLString,int commTime)
    ...{
        string connectionString = System.Configuration.ConfigurationManager.AppSettings["connectionString"];
        using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
        ...{
            DataTable dt = new DataTable();
            try
            ...{
                connection.Open();
                System.Data.SqlClient.SqlDataAdapter da = new System.Data.SqlClient.SqlDataAdapter();
                System.Data.SqlClient.SqlCommand comm = new System.Data.SqlClient.SqlCommand(SQLString, connection);
                comm.CommandTimeout = commTime;
                da.SelectCommand = comm;
                da.Fill(dt);
            }
            catch (System.Data.SqlClient.SqlException ex)
            ...{
                throw new Exception(ex.Message);
            }
            return dt;
        }
    }.net

相關文章
相關標籤/搜索