利用对象池优化数据库操作

 

 

简介:这是利用对象池优化数据库操作的详细页面,介绍了和asp.net,.Net,创建,对象池,示例有关的知识,要查看更多相关信息,请点击此处

说到对象池,大家都不陌生。很多人都实现过,网上的代码也满天飞。说到连接池,更是谁人不知,哪家不晓。也有不少人自己实现了连接池,试图对数据访问进行优化。归纳了一下,比较常见的思路如下:

<!--[if !supportLists]-->1.                   <!--[endif]-->数据库连接的打开比较耗费资源,如果能避免重复的开关,可以提高效率。

<!--[if !supportLists]-->2.                   <!--[endif]-->如果有一个一直打开数据库连接,直到程序结束其生命才随之结束。长时间打开并重复使用将导致连接对象的不稳定。

<!--[if !supportLists]-->3.                   <!--[endif]-->无法保证打开的连接一点问题都不出。

<!--[if !supportLists]-->4.                   <!--[endif]-->自动化管理数据库连接,解决频繁创建、分配、释放带来的问题

然而本人认为,这些担心相对来说并不是首要的。微软在.net Framework平台下既然推荐使用断开模式,按照微软的作风,必然有一个适当的解决方案来。首先Ado.net本身实现了连接池,自动管理数据库连接的生命周期(虽然如果不加控制,随意打开连接,也会有池被填满的时候),其次Ado.net的连接池也解决了数据库连接反复打开的效率问题。本人认为:虽然常见的连接池都以控制连接的实例数为目标,但池中的元素其实是共享的,也无法控制并发的操作数,并不见得就能为性能带来什么本质的提高。在数据库操作中有一个更重要的东西:控制同一时间的并发操作数。

首先,来看看Ado.net的工作方式。Ado.net在数据访问上提供了2种方式:1、连接模式:一般采用DbDataReader对数据进行读取,读取的过程中,数据库连接始终是打开的。在读取数据前,打开数据库连接。读取结束后在关闭数据库连接。2、断开模式:利用DbDataAdapter将数据一次性取出,填充到DataSet中,然后关闭数据库连接,DataSet中的数据和数据库是完全断开的。由DbDataAdpater在是获取数据前,判断数据库连接是否打开,如果处于关闭状态,则将连接打开,填充完DataSet之后,再将数据库连接关闭。如果连接在获取数据之前已经打开,则不打开连接,填充完DataSet后也不将连接关闭。因此Ado.net的工作方式可以归纳为以下程:1、打开连接。2、执行SQL语句返回结果。3、关闭连接。可以说这3个步骤组成了.net下数据操作的标准模式。虽然数据操作只是在重复这3个 基本步骤,但是在单线程和多线程的应用场合下实现的细节也有所不同。单线程应用开发时,常用的做法是整个应用程序共享一个数据库连接;由于一个应用程序 中,用户同一时间只能进行一次数据库操作,因此这种方法是可行的。而进行多线程应用开发时,由于是多线程并发操作数据库,因此每次数据库操作都实例化一个 数据库连接,然后再执行标准的3个步骤。

从Ado.net的工作方式不难看出,如果同时进行的多个数据操作,共享一个数据库连接,是绝对不允许的。虽然Ado.net提供了MARS(即:Multiple Active Result Sets)来解决这个问题,在数据库连接字符串中加入“MultipleActiveResultSets=true”。但并不是所有数据库都支持MARS,常用的Sql server2000数据库就是一个例子,而且启用MARS会大大降低系统的性能。因此当数据库不支持MARS时或出于性能上的考虑又或者不是读取而是进行写操作,最常用的做法就是每一个数据操作都实例化一个数据库连接。

然 而,看似合理的做法又引来了新的问题。首先服务器资源是有限的,每次数据操作都实例化一个连接都会占用服务器资源,特别是在长时间的数据存取操作上。如果 实例化并打开了一个数据连接,但忘记关闭,将占用服务器的连接直到垃圾回收机制自动回收的时候才会释放当前占用的连接,容易造成所谓的连接泄露。其次,客 户端进行异步调用的时候往往无法预测并发的数量,无限制发起异步操作并同时进行将导致服务器超负荷工作,不仅没有起到优化的作用,还带来了更多负面的影响 (如:连接超时、响应变慢、内存不足等一些列问题)。

通过简单的测试可以得到一下结果:即使SQL语句执行时间较短,同时进行大量的并发数据库操作,但不加控制,服务器基本就当机了。当SQL语句执行时间较长时,可能希望利用并发的异步操作来优化效率,如果不加控制,实际结果往往是连接超时的几率大大增加,却不见得能带来更高的效率。或者说一个客户端开始发飙后,其他的客户端基本就无法连上了。

总结起来,必须用一个比较合理的手段来控制实例化的连接数,同时又能控制并发的数据库操作数。实现一个对象池,池中的元素都是以独占方式取出。池的策略分为2种:1、速度优先:只要有空闲的元素就直接使用。2、使用次数优先:在已使用的元素中选择使用次数最少的。从池中获取可用的数据库连接时,首先判断是否有已初始化的空闲连接,其次判断是否还有空位能容纳新的连接。如果前二者都无法满足,则进入等待并利用Sleep暂 停当前线程,直到有元素空闲。在对象池初始化的同时,启动一个轮询线程,用于定时对对象池进行执行清理工作,如:在作为连接池时可以关闭超时的连接,释放 未使用的连接。具体使用时,只要简单将原来实例化一个DbConnection改为从池中获取一个可用的连接。并在使用完成之后将连接归还给池。由于池中 的每个元素都是独占的,当池中没有空闲的元素时就必须等待,直到有空闲的元素出现。因此,设置合适的池大小,在一定程度上等于限制了并发的操作数。

恰当地使用对象池技术,能有效地改善应用程序的性能。目前,对象池技术已得到广泛的应用(如:对于Web、网络和数据库等,一般都会采用对象池技术),但不应滥用。Ado.net本身实现了连接池,并为我们提供了一个高效、健壮的机制。在Ado.net开发时应该将主要的精力放在如何控制并发的数量和优化SQL语句上以及事务的处理上。

最后,丢一个土了吧唧的对象池工作流程图,让人批斗^_^ 。

你可能感兴趣的:(数据库)