2012年07月21日 10:07:07
连接池技术,大大的提升了应用程序的性能,但是如果不了解连接池的使用场景和原理,就茫然使用连接池,.net默认使用连接池,大多数人会选择使用连接池的默认设置,这样带来的后果往往也是惨痛的,也许你将花费更大的代价去查找因为连接池的不当使用而带来的未知问题。
在一个项目中,因为连接池的不当使用而带来了一个惨痛的教训。一般我们的应用程序如果部署在一个较为稳定的网络环境中,默认使用连接池可能不会有什么问题,如果一旦部署到一个不稳定的网络环境,你就必须得绞尽脑汁去面对那些应用不稳定代带来的折磨。
首先,大概说一下连接池的原理
数据库连接池允许应用程序重用已存在于池中的数据库连接,以避免反复的建立新的数据库连接。这种技术能有效提高应用程序的伸缩性,因为有限的数据库连接能够给大量的客户提供服务。这种技术同时也提高的系统性能,避免了大量建立新连接的开销。
开发一个具有伸缩性的、高性能应用程序应该最大限度的减少建立连接所花费的时间,保持数据库连接最大限度的有效,以存取数据。当一个数据库连接关闭时,它只是由连接池收回以待重用,并未真正释放。但是,如果连接池被释放,数据库连接将会被释放掉。
开发人员应当注意不要依赖垃圾回收机制去释放数据库连接,因为当参数超出作用域时,数据库连接并没有得必要的关闭,这种数据库资源泄漏将导致建立新连接时抛出连接错误。
建立数据库连接池
当打开一个数据库连接时,一个数据库连接池也就创建了。数据库连接池的创建与数据库连接字符串精确的相关(包括空格、大小写)。所有的连接池是根据连接字符串来区分的。在创建一个新的数据库连接时,如果连接字符串不完全相同,将创建不同的连接池。
一旦数据库连接池被创建,它将一直存在直到该进程结束。维护一个非活动状态的连接池几乎不需要什么系统开销。
连接池中的数据库连接
连接池根据唯一的连接字符串被创建。在连接池被创建的同时,连接池将创建最小的数据库连接,当连接不够用时,连接池将逐个添加数据库连接直到达到最大连接数,此后的连接请求将被加入请求队列里。当调用数据库连接对象的Close方法或Dispose方法时,数据库连接将被数据库连接池回收。
当数据库连接使用完成后,要调用Close方法或Dispose方法将它返回连接池。没有显式释放的数据库连接可能会没有返回连接池。
应用程序在建立连接时,如果启用连接池,会在连接池维护一个逻辑连接和一个物理连接。
在不稳定网络环境中,如防火墙,网闸(内外网部署应用)物理隔离,在级联交换机出现在网络阻塞的情况下出现网络瞬断的概率较大
内外网系统交互,出现网络瞬断时,再建立网路连接就很容易出现ora-03113 通信通道的文件结束或ora-03114无法连接到oracle,这两个错误在网上的解释大部分都是因为网络导致。那么针对这两种错误,如果没有必要使用连接池就设置连接字符串Pooling=false,因为在使用连接池时,一旦出现网络瞬断,连接池里的连接物理连接已经中断,但逻辑连接依然存在,当下次有新的请求时,连接池就很有可能将死连接分配出去,就会出现上面的错误。如果出现ora-03114只能重新连接。
以下之前解决这个问题时的步骤,之前写的一个总结,但是最终依然没有根治,最终不使用连接池不再出现错误,
短信中心服务开发完成后,在测试经常发现一个oracle错误:ORA-03113,而且只要一发生这个错误,短信服务就不能正常提供服务。发生的这个错误还是很有规律的,就是在业务繁忙时是不会发生这个错误的,只有在服务长时间空闲时才发生(一般是超过5分钟没有操作的情况下)。开始以为是oracle会自动切断不活动的连接,但是查看oracle的session日志后,发现oracle并没有主动切断数据库连接,而且这个数据库连接是“莫名其妙”的断开的,也即不是客户端断开的(由于程序中使用了数据库连接池,连接池会每隔一定时间将不活动的连接关闭)。然后为了查看到底是什么问题,我们就在数据库端和客户端同时监视连接的情况(在数据库端监视数据库连接,在客户端监视TCP连接数,oracle使用的1521端口通信)。这时候发现了一个有趣的问题:就是当出现ORA-03113错误时,数据库端的session显示的连接很好,但是在客户端就会发现大量的僵死的TCP连接(连接未完全关闭,状态显示是CLOSING)。这时我们就猜测可能是中间的网络环境导致TCP连接非正常关闭。咨询了“网管”后,得知应用服务器和数据库服务器之间要通过网闸,但是网管说网闸不会切断连接的。经过查询资料后(请看这一篇文章:http://www.laoxiong.net/oracle_and_firewall.html),发现我们的情况和文章上的很相似,我们就怀疑网关有TCP连接拆除功能(这个需要在落实下,摸清网闸的功能和网闸到底能对网络产生多大的影响)。经过一番测试后,最中定位到网闸应该有定时拆除TCP连接的功能(因为如果业务比较频繁的话,数据库连接是畅通的,只要在业务空闲的情况下才会出现这个问题)。
问题解决思路:
第一步:我们打算先从连接池配置开始,这些僵死连接对连接池最大的影响就是使连接池认为这些连接还是畅通的,当程序打开一个连接时,连接池就会返回一个僵死连接,这时候在僵死连接上操作就会发生ORA-03113错误。我们的思路就是将连接池关闭连接的时间缩短。尽量市空闲连接在僵死前将其关闭然后重新建立连接(注意:这样会使连接池性能有所下降)。但是经测试,这个方法的效果并不是很好,因为连接僵死的时间并不固定,所以无法设置一个合适的时间让连接池关闭连接。
第二部:使用oracle DCD功能,也就是设置sqlnet.ora的sqlnet.expire_time参数,让oracle定期发送心跳包使连接保活,但是这个方法还是有一个问题的,就是应用的心跳包会超时(TCP的keepalive属性导致)。所以为了使oracle和客户端尽早发现连接断开,需要设置TCP的keepalive参数(Windows需要设置注册表,LINUX需要设置相应的配置文件),这样可以解决短连接问题(这里的短连接指程序使用的短连接,也就是在需要使用数据库时才申请连接,快速完成事务,然后关闭连接,由于使用了连接池,所以连接并没有关闭,只是归还到连接池而已)。
第三部:短连接问题解决了,但是长连接还是照样会出现ORA-03113错误(使用长连接的是oracle AQ轮询线程,该线程会不停对AQ队列进行一次出队操作,超时时间是5m,以此来从队列里获得短信数据)。经试验检测是因为在等待的这段时间内,数据库连接断开所导致。所以我们就将出队操作的时间缩短到1m,这样就是该连接一直有活动,这样就使ora-03113的错误发生概率降到了最低。
最终的解决方案:
1. 使用oracle 的DCD国内(设置sqlnet.expire_time参数),设置操作系统的keepalive参数。
2. 针对短连接和长连接分别采用不同的数据库连接池策略,短连接使用数据库连接池,并设置连接保活时间,这样可以是连接池尽可能早的发现僵死连接。对于常连接,则不使用数据库连接池,一旦发生ORA-03113错误,说明此连接已经失效,这时就关闭此连接,并申请新的连接。
3. 缩短oracle AQ的出队等待时间,这样可以是连接经常有操作。但是这样又出现了第二个问题ORA-25228错误(队列出队超时)。随意最后队列的异常处理为:ORA-03113 关闭连接,重新申请连接,ORA-25228 不进行任何操作,直接进行下一次的出队操作。