认识数据库连接
数据库连接,这还用说,都会接触到啊,说的这是这个经常见的数据库连接。
Data Source=.;Initial Catalog=test;Persist Security Info=True;User ID=testuser;Password=123456;Min Pool Size=10;Max Pool Size=150;Connection Lifetime=10
这谁不明白啊,这里要说的就是
Min Pool Size=10;Max Pool Size=150;Connection Lifetime=10
这里就说到 数据库连接池 了,默认是启用的,以上的属性就是配置这个连接池的。这也就意味着,当你的页面发送数据库请求的时候,不一定就非要创建数据库连接,而可能是从已存在连接池里,激活一个的连接来处理你的请求的;同样,当你代码中调用Close显式关闭数据库连接的时候,也不一定就是真的关闭数据库连接了,如果当前连接池中没有满足Min Pool Size要求的连接时,它只是返回到连接池,等待下一个命令来激活它。
通过
sp_who 'testusers' // testusers是你建立连接时指定的用户名
可以查看你当前连接数
连接池是怎么按照你的配置如何工作呢??
当第一次连接请求到来时,它会在很短的时间内创建Min Pool Size指定的连接,尽管你的并发请求数可能远没达到这个数目。当并发请求数低于这个值,这以后将不会有连接的的创建和关闭,除非你关闭应用程序;并发请求数目大于这个最小值时,就会创建连接,并发请求数大于Max Pool Size最大值时,这个请求将进入等待队列,等待空闲连接,如果达到应用程序或者数据库限制的时 间还没被处理,就将返回连接超时的异常。
这原理谁不懂啊,是的,程序正常的时候是这样,当程序不正常的时候是什么样呢。。正常的程序:凡是操作数据库的,操作完毕之后都有显式的调用Close()或者Dispose()关闭连接。下面做个试验,同样下面提供的方法,可以验证你的应用程序是否存在没有关闭的连接。本人也是基于这个需求,才做这些试验的。
现如今的程序,涉及数据操作的都是封装好啊,是的,像dataset,datatable等reader以外的操作,都是在数据库操作类处都关闭了。嗯,是的,但是reader,就只有在开发人员用完之后显示关闭了,这就可能由开发人员的素质决定了。不要告诉我,你根本就不用reader,呵呵,那这篇文章跟你没关系了。
就以reader来开始实验,我们把Min Pool Size设为1.
首先是正常的程序:
using (SqlDataReader reader = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from protuct order by id desc")) { while (reader.Read()) { Response.Write(reader["Name"].ToString()); } } using (SqlDataReader reader2 = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from users order by userid desc")) { while (reader2.Read()) { Response.Write(reader2["userName"].ToString()); } }
这段程序放在一个页面里,无论你怎么刷或者多少人同时请求这个页面,待请求处理结束后,通过sp_who查看到的,还是那一个。知道这为什么叫正常的程序了吧,符合正常推理,哈哈。
以下是有问题的程序:
SqlDataReader reader3 = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from users order by userid asc"); while (reader3.Read()) { Response.Write(reader3["userName"].ToString()); } SqlDataReader reader4 = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from protuct2 order by id asc"); while (reader4.Read()) { Response.Write(reader4["Name"].ToString()); } SqlDataReader reader5 = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from protuct order by id desc"); while (reader5.Read()) { Response.Write(reader5["Name"].ToString()); } SqlDataReader reader6 = Maticsoft.DBUtility.DbHelperSQL.ExecuteReader("select top 1 * from users order by userid desc"); while (reader6.Read()) { Response.Write(reader6["userName"].ToString()); }
可以看到,reader没有显式关闭,为了看效果,多放了几个。
首先,我先一个人正常的刷一下,页面加载完毕后,通过sp_who查看,连接有4个,状态是sleeping,wait command,大于Min Pool Size了,不正常。我如果再刷一下,你猜会是几个,8个??大于4个??再一次次正常的刷,结果还是4个????说明连接执行完毕后会自动返回连接池,没有关闭的请求,就存活了下来,但是有执行操作的请求时,它们依然可以被激活来执行操作。哇哈哈,那岂不是很爽,没有显式关闭连接不会造成任何不良的影响啊。哈哈,但是,没有显式关闭并不代表不关闭啊,根据.net垃圾收集的工作原理,我们知道任何对象在离开它的工作范围之后就成了垃圾,等待被回收,回收的过程中,回收管理器发现对象实现了IDipose(),就会再次激活这个对象,调用它的Dispose(),然后再次成为垃圾,最终被回收。Dispose()调用的时候,是不确定的,因此,连接池中多余的连接也会在未来的某个时间关闭,那么就有可能造成程序的不稳定,因为这个连接可能此时已经被激活处理其它请求,又可能被后来的请求已显式关闭等等,也或许微软已通过某种机制让上述担心成为了多余。但是不正常代码,影响远不止这些。。。。。
请注意前面是一次次正常的刷新,也就是我自己等页面出来以后,再刷新页面,下面我强制按住F5,让页面连续刷新,再次通过sp_who查看,结果大吃一惊,此时sleeping的连接,达到了几十个,如果很多人呢同时访问呢,那连接池很快就会被这占满,不稳定的影响,很快就会被放大很多倍。因些如何判断你的应用程序存在未关闭的连接,在访问高峰期或者自己模拟大量的并发请求,查看如果状态是sleeping的连接大于你设定的Min Pool Size值,肯定就是存在了。
下面再说说Connection Lifetime
官方解释:每当一个连接使用完后释放回连接池,如果当前时间减去该连接建立的时间的值大于这个参数设定的值(秒),该连接被销毁。0表示lifetime没有上限。
根据表面意思,没显式关闭的连接也会在这个值指定的时间来临时关闭,那太好了,管它reader关没关呢,设个较小lifttime不得了。但是令人疑惑的是,我把此值设为1,不正常代码的产生的多余连接并没有在1秒后销毁,而是等待一个不太确定的时间,这个可能就是垃圾收集来临的时间,同样正常代码情况下,将此值设为60,显式调用的如果超出Min Pool Size的连接会被立即关闭,在Min Pool Size范围内的连接一直存活。这么看来此值没一点作用啊。
搜索了搜索发现有篇文章提到了作用:
实验结束,再一次用事实证明连接一定要显式关闭,同样提供了一个简单方法查看我们的程序是不是存在未关闭的连接。