asp.net连接池和SqlConnection对象的释放& "Timeout expired"异常

  原文出处:
原文标题:关于ADO.Net连接池(ConnectionPool)的一些个人见解
一下是原文:
建立池连接可以显著提高应用程序的性能和可缩放性。SQLServer.NETFramework数据提供程序自动为ADO.NET客户端应用程序提供连接池(MSDN)。
Openingadatabaseconnectionisaresourceintensiveandtimeconsumingoperation.ConnectionpoolingincreasestheperformanceofWeb/windowsapplicationsbyreusingactivedatabaseconnectionsinsteadofcreatinganewconnectionwitheveryrequest.Connectionpoolmanagermaintainsapoolofopendatabaseconnections.Whenanewconnectionrequestscomein,thepoolmanagerchecksifthepoolcontainsanyunusedconnectionsandreturnsoneifavailable.Ifallconnectionscurrentlyinthepoolarebusyandthemaximumpoolsizehasnotbeenreached,thenewconnectioniscreatedandaddedtothepool.Whenthepoolreachesitsmaximumsizeallnewconnectionrequestsarebeingqueuedupuntilaconnectioninthepoolbecomesavailableortheconnectionattempttimesout.
Connectionpoolingbehavioriscontrolledbytheconnectionstringparameters.PleaselookintoMSDNdocumentsinthereferencelinkifyouwanttoknowfurtherinformation.

前面是关于连接池知识的一些基本介绍,下面的内容是重点,也是个人见解。因为没有这方面的文档资料参考或者佐证,所以请各位仔细思考和讨论(我不想误导大家)。
下面分2种情况进行讨论。
1,Client端的windowsformapplication通过ADO.Net直接访问后台Database。

查看大图
SqlServerDirectConnection

  

  我认为在这种情况下,每一个Client端和Database之间都存在一个连接池。通过设置ConnectiongString的MaxPoolSize和MinPoolSize属性来验证。
如MaxPoolSize=5,MinPoolSize=3,通过SQLServer的SP_WHO2可以检测导如下结果:
启动1个Client端的Windowsformapplication:application和SQLServer之间存在3个connection。
启动2个Client端的Windowsformapplication:application和SQLServer之间存在6个connection。
启动3个Client端的Windowsformapplication:application和SQLServer之间存在9个connection。

由此可见,每一个Client端和SQLServer之间都存在一个连接池,否则9个connection已经超出了MaxPoolSize的设定。

2,Client端的application不直接通过ADO.Net来访问后台Database,而是通过IISServer来同后台DatabaseServer打交道。
至少有如下2种情况:
(1)Client通过IE访问部署在IIS中的webapplication,webapplication中的DataAccessClass进一步访问后台DatabaseServer.
(2)Client端的windowsformapplication访问部署在IIS中的RemoteObject,RemoteObject进一步访问后台DatabaseServer.

查看大图
SqlServerInDirectConnection
下面进行同样的测试:通过设置ConnectiongString的MaxPoolSize和MinPoolSize属性来验证。
如MaxPoolSize=5,MinPoolSize=3,通过SQLServer的SP_WHO2可以检测导如下结果:
启动1个Client端的Webformapplication:application和SQLServer之间存在3个connection。
启动2个Client端的Webformapplication:application和SQLServer之间存在3个connection。
启动3个Client端的Webformapplication:application和SQLServer之间存在3个connection。
调用由IIS承载的RemoteObject,结果类似。只是在未启动Client端之前,发现在RemoteObject与DatabaseServer之间已经存在一个connection。
由此可见,对同一个application而言,IISServer与DatabaseServer之间只有存在一个连接池,与Client端的多少没有关系。
3,连接池小节
根据上面的测试结果,显然第二种情况更有利于减少无用的连接数量,提高DatabaseServer的性能。关于.NetRemoting技术及其性能问题,可以参考如下的Reference连接,这里就不讨论了。
另外,本文是个人关于连接池的一些见解,如果观点有不正确之处,希望不要误导各位。欢迎各位在此发表意见。同意的说赞同,不同意的请提出您的看法。谢谢。
ReferenceLinks:
(1)MSDN,ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpguide/html/cpconconnectionpoolingforsqlservernetdataprovider.htm
(2)Microsoft.NETRemoting:技术概述,
(3)性能比较:.NETRemoting与ASP.NETWeb服务,
========================原文结束,开可以参考另外一篇文章:NET连接池救生员
原文的一些补充观点:
连接的创建和获取:
Q:如果我最大连接池是5个,那么,在我的程序中,每次访问,我都会使用New一个连接,然后Open,使用以后,然后Close,那么在每次New的时候,会不会创建连接,?
A:在每次New的时候,如果connectionpool中有空闲的连接,则不会创建新连接,否则会创建。
如果超过最多连接数,还能发出New的请求,但是该请求会放入队列,直到connectionpool中有空闲连接或连接请求超时。
连接的效率问题:
Q:要是可以对连接进行控制的话,应该怎么控制,很简单,我现在每次使用数据库访问时,都会New并close,我是担心这样的效率是不是会很低啊,想在连接上提高效率,要怎么操作呢?
A:使用connectionpool时,New并不一定会创建新的连接,如果connectionpool中有空闲连接,直接拿来就用。
close也只是将连接放入connectionpool,供后续请求使用。因此不会有效率问题。
连接的存活时间:
当连接返回到池中时,将对它的创建时间和当前时间进行比较,如果时间间隔超过由ConnectionLifetime指定的值(以秒为单位),则会毁坏该连接。在聚集配置中可以使用它来强制在运行服务器和刚联机的服务器之间达到负载平衡。
如果值为零(0),则将使池连接具有最大的超时期限。默认值为0
SqlConnection.Close和.Dispose的比较:
sqlconnection.close和.dispose的相同地方是:
dispose肯定调用了close,所以close里面有做的事情,dispose都包括
dispose还做了其他的资源的释放,这样在GC第一次回收的时候,省却了dispose的步骤,加快了内存资源的回收速度。
如果没有调用dispose,GC将在第一次回收先做dispose。
如果程序频繁做newsqlconnection(),然后很快就做close并且不再使用这个connection(例如一些com+/asp.net/webservice的程序),而且又不做dispose,那么随着这个程序被多次调用,被分配但未能尽快释放的系统资源(通常是内存)会有很多,GC被迫回收的次数也相应增多,系统的整体效率就会变低(GC回收的时候是所有.net程序都暂停,等GC回收跑完才可以继续,造成其他程序也受到影响)。所以对于这种情况,做dispose好。
但如果其他情况,例如Winforms的client,那么没有所谓。
最近发现本站有时候会出现连接不上服务器的现象,通过sp_who查看数据库连接时,发现网站应用程序占用了比较多的连接数,当连接数达到一定数量后,网站就会出现连接不上数据库的错误。那么可以肯定,网站出现错误的原因,是数据库连接没有及时释放的缘故或者是开辟了太多的连接。网站的数据库访问逻辑没有集中在一个数据库访问类中,而是在用到的时候建立,用完了关闭。那么这种情况是否跟上文讲的第一种情况相同呢?上文的第一种情况应该是针对winform应用程序来说的。但是本站的程序没有想上文说的第二种情况,把数据访问集中在一个数据访问类中。这个问题有待实践来验证。
来源:

作者:angelsb


System.InvalidOperationException:Timeoutexpired.Thetimeoutperiodelapsedpriortoobtainingaconnectionfromthepool.Thismayhaveoccurredbecauseallpooledconnectionswereinuseandmaxpoolsizewasreached.

Timeoutexpired异常是个很棘手的异常,想必几乎每个人都碰到过。有时可真是对它咬牙切齿,拿它没办法。angelsb这篇文章很好,希望对大家有用。我也是看到他讲得很好,才翻译过来的,水平有限,请多多指教.


System.InvalidOperationException:Timeoutexpired.Thetimeoutperiodelapsedpriortoobtainingaconnectionfromthepool.Thismayhaveoccurredbecauseallpooledconnectionswereinuseandmaxpoolsizewasreached.

哎!在另一个进程中,又出现了连接池已满的问题,这是个最让人头痛却又是最常出现的连接池问题之一.原因是在开发过程中很少碰到这个头痛的问题,但在部署APP到客户端时,却总是不经意地跑出来了.我想,我应该花些许时间对这个问题进行一次完整的总结吧.

发生的本质是什么?

我们来认真看一下可能会发生这种异常的两种情况

1)你使用了超过最大的连接池连接数(默认的最大连接数是100)

在大部分应用程序中,这种情况是很少出现的.毕竟当你使用连接池时,100个并行连接是一个非常大的数字.根据我的经验,会造成这种异常的原因的最大可能,应该是在一个纯种下打开了100个连接.

  程序代码

SqlConnection[]connectionArray=newSqlConnection[101];
for(inti=0;i=100;i++)
{
connectionArray[i]=newSqlConnection("Server=.//SQLEXPRESS;Integratedsecurity=sspi;connectiontimeout=5");
connectionArray[i].Open();
}

  


解决方案:如果你确定你将会使用超过100个并行连接(在同一连接字符串上),你可以增加最大连接数.

2)连接泄漏

我个人认为的连接泄漏定义是你打开了一个连接但你没有在你的代码中执行close()或dispose().这范围不仅仅是你忘记了在connection后连接后使用dispose()或close()对期进行关闭,还包括一些你已经在相关connection后写好了close()却根本没有起作用的情況.我们来看看下面的代码:

  程序代码

usingSystem;
usingSystem.Data;
usingSystem.Data.SqlClient;

publicclassRepro
{
publicstaticintMain(string[]args)
{
Reprorepro=newRepro();
for(inti=0;i=5000;i++)
{
try{Console.Write(i+"");repro.LeakConnections();}
catch(SqlException){}
}

return1;
}
publicvoidLeakConnections()
{
SqlConnectionsqlconnection1=newSqlConnection("Server=.//SQLEXPRESS;Integratedsecurity=sspi;connectiontimeout=5");
sqlconnection1.Open();
SqlCommandsqlcommand1=sqlconnection1.CreateCommand();
sqlcommand1.CommandText="raiserror('Thisisafakeexception',17,1)";
sqlcommand1.ExecuteNonQuery();//thisthrowsaSqlExceptioneverytimeitiscalled.
sqlconnection1.Close();//Wearecallingconnectionclose,andwearestillleakingconnections(seeabovecommentforexplanation)
}
}

  


这就是一个典型的例子,将这段代码复制到visualStudio中,在sqlconnection1.close()中设置一个断点,编译时可以看到他永远没有执行,因为ExecuteNonQurery抛出了一个异常.之后你应该可以看到恐怖的超时异常了.在我的机子上,大约有170个连接被打开.我曾想让其在每次调用的时候将异常抛出来达到降低连接超时出现的机率,但当你考虑到将其部署到一个ASP.NET的应用程序的时候,任何一个泄漏都将让你处于麻烦之中.

3)你是通过visualStudio中的sqldebugging来打开或关闭连接的

这是一个众所周知的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设为关闭状态,你当然不会再碰到超时异常,可怕的是你的应用程序性能将大大降低,而你的连接仍然处于泄漏状态.

不要把ConnectionLifeTime=1

这不是一个能清除异常的方法,但它可能是最接近的一个解决方法.你想告诉我们的是将所有的连接超过一秒钟的连接都通通抛弃(正常的生命周期结束应该是在connetcio.close()后).我个人认为这种方法上关闭连接池没什么两样.除非你是在使用数据库的集群,否则你不应设置连接周期来达到目的.


不要将ConnectionTimeOut=40000


非常愚蠢的选择,你这是在告诉我们在抛出一个超时异常之前,你在无限地等待一个连接转变为可用的.幸亏在ASP.NET中将会在三分钟之后取消一个进程.


不要将MaxPoolSize=4000;
如果你将连接池的最大数设置到足够大的时候,你最终会将这异常停止.但在另一方面,你将占用了你的应用程序中才是真正需要的巨大的连接资源,这种做法只能饮鸠止渴.



解决方案:

你需要保证你每次调用连接的同时都在使用过后通过close()或dispose()对其执行了关闭.最简单的办法就是使用using,将你的连接泄漏方法修改成如下面的代码样式:

publicvoidDoesNotLeakConnections()
{
Using(SqlConnectionsqlconnection1=newSqlConnection("Server=.//SQLEXPRESS;Integratedsecurity=sspi;connectiontimeout=5")){
sqlconnection1.Open();
SqlCommandsqlcommand1=sqlconnection1.CreateCommand();
sqlcommand1.CommandText="raiserror('Thisisafakeexception',17,1)";
sqlcommand1.ExecuteNonQuery();//thisthrowsaSqlExceptioneverytimeitiscalled.
sqlconnection1.Close();//Stillnevergetscalled.
}//Heresqlconnection1.Disposeis_guaranteed_
}



FAQ:

Q:为什么要这样做

A:使用using结构等同于Try//Finally{usingobject.Dispose())即使当ExecuteNonQuery会抛出一个执行错误时,我们都可以保证finally模块将会执行



Q:我上面的代码中如果没有异常抛出的话,我可以使用close()或dispose()吗

A:我们毫无顾忌地用他们中的任意一个,或两个同时使用.在一个已经close或dipose()的连接中使用close()或dispose()是不会影响的

Q:Close()和Dispose()有什么不同,我应该用哪一个好?
A:它们做的是同一件事,你可以调用他们中的任意一个,或两个同时使用.

Q:你所说的"practicallythesamething是什么意思?
A:Dispose()将会通过sqlConnection来清理相关的连接,之后执行close().它们没有什么本质的区别,你可以通过reflector来证明这点

Q:与close()相比,connection.dispose()会将连接些移除吗?
A:不会

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

我的分享:

针对"Timeoutexpired"这个异常,我也查阅了很多资料。在国内我们很多project都会采用MS提供的sqlhelper这个封装类。因为这个类中有本身的缺陷所致,所以出现的"Timeoutexpiered"异常机率大。我在国外的一篇文章中看到的解决方案是:

将SqlHelper中的cmd.CommandTimeout="你要设置的秒数"加上去,重新编译.


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

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


通过搜索,我找到了相关的一个代码.
这个代码是摘自国人的某位同行的,感谢


eg:

**summary
///执行查询语句,返回DataTable
summary
///paramname="SQLString"查询语句/param
///paramname="commTime"设置查询Timeout/param
///returns用于复杂查询/returns
publicstaticDataTableGetDataTable(stringSQLString,intcommTime)
...{
stringconnectionString=System.Configuration.ConfigurationManager.AppSettings["connectionString"];
using(System.Data.SqlClient.SqlConnectionconnection=newSystem.Data.SqlClient.SqlConnection(connectionString))
...{
DataTabledt=newDataTable();
try
...{
connection.Open();
System.Data.SqlClient.SqlDataAdapterda=newSystem.Data.SqlClient.SqlDataAdapter();
System.Data.SqlClient.SqlCommandcomm=newSystem.Data.SqlClient.SqlCommand(SQLString,connection);
comm.CommandTimeout=commTime;
da.SelectCommand=comm;
da.Fill(dt);
}
catch(System.Data.SqlClient.SqlExceptionex)
...{
thrownewException(ex.Message);
}
returndt;
}
}


水平有限,多多指教.一点经验,与大家分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值