数据库连接池更多的是一个本地的概念。以前一直觉得,数据库连接池是数据库服务器上的一个概念,在数据库服务器上有一个池,里边存放着很多的线程的数据库连接。
最近在分析的时候,发现这是不正确的,或者说,这样理解是不全面的。其实,连接池更多是一个运行C#代码的那个本地机器上的概念,当然数据库服务器端也有涉及,因为毕竟连接是双方的,但是更多的这个是从连接的本地角度出发的。
下面以一个网站的例子来具体分析一下这个问题。现在有一个网站,里边有C#代码,通过代码访问远端的SQLServer数据库,来获取所有的销售人员的数据。现在我们把网站部署到服务器A上边,数据库存在服务器B上边。
假如我们不使用数据库连接池,每次当有人打开浏览器输入网站,在A服务器上边就有一个相应的线程来处理这个请求,那么在这个线程中就会有一个从A到B的数据库连接的打开与关闭。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,费时间只是一个方面,而且还要费资源,系统要分配内存资源。这个对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。如果同时有1000人访问,就会不断的有数据库连接、断开操作。
所以,这时候的思想就是,C#代码在本地建立了一个连接,在建立这个连接的同时,那么我们把这个连接保存到一个公共的区域,让每一个web请求的线程都能访问到这个区域。而且,当下一次有一个新的线程要访问数据库,直接使用这个已有的连接就好了。为了避免同时有多个web线程都要请求数据库的时候,会出现资源的争夺,线程池那个公共区域中我们可以设置多个连接,这样就可以避免资源的争夺与等待。
但是从数据库服务器的角度来讲,它并不知道跟它建立连接的是一个单次的线程,还是数据库连接池里边的连接。它只知道有个连接在跟它连着,而且在对方没有发出断开的请求时,它就一直连接着。(这里可能说的不太准确,数据库服务器能够知道建立的是普通连接还是连接池的连接,但是这些对于宏观角度来讲都不重要,都是一个从远端客户端来的连接。)
现在的C#语言或者说Java语言中,在类库中对于这些连接池的封装已经做得很细致,甚至都不用程序员去写额外的代码。这里拿C#代码连接Oracle数据库举个例子。你要想使用C#连接远端的Oracle数据库,第一你需要在本机安装ODP.NET这个叫做Provider的组件,第二你需要在C#代码中引入Oracle.DataAcess.dll这个类库。
当你的C#代码在使用类库中的类建立连接,并且使用Open方法打开连接的时候,这时候连接池就会初始化并建立设定的最小连接数。这时候可能就是为什么要你安装那个Provider,因为一个简单的数据库连接背后还有跟多的内容要做,可能一个dll是无法完成这个复杂的内容的。所以安装了Provider之后,在运行C#代码的时候,C#代码引入类库中的方法的时候,会调用Provider中的一些小的应用程序或者说程序集去完成复杂的内容。
最近发现Oracle数据库的一个问题,在这篇博客。http://blog.csdn.net/sundacheng1989/article/details/52755867
下面解释一下原因:
说到底,每次C#代码new一个连接的时候(我们假设程序运行了一段时间并且连接池中有足够的连接存在),并且Open的时候,都是去从连接池中去拿一个现成的连接。在使用完连接时一定要关闭连接,以便连接可以返回池。要关闭连接使用Close(),这时候不是真正的关闭了,还是还给了连接池。Oracle那个问题的所在就是,C#整个程序(或者也有Provider的参与)维系着一个连接池,这个连接池中有很多的连接,在连着Oracle的数据库服务器。
因为很长一段时间内,Oracle数据库服务器那端都没有检测到这些连接有活动,就把这些连接给关闭了。但是应用程序维系的连接池并没有觉察到,所以一天后当再次new一个连接,conn.Open的时候,应用程序还是从连接池中拿了一个,并不知道拿的这个已经关闭了,C#代码继续进行着进一步的查询操作,这时候发现连不上了,所以,就抛出了这个异常。这时候可以通过禁用连接池来解决这个问题,但是性能上就不太好了。
在主流的C#或者Java中,你生成一个连接的时候,默认的都是用连接池维护的,除非你明确指明了不使用连接池(在配置文件的连接字符串中加入threadingPool=false),这时候才会是每一个线程都建立一个连接并且完事后销毁这个连接。