链接池和 "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

相关文章
相关标签/搜索