《C#java实现数据库连接池》由会员分享,可在线阅读,更多相关《C#java实现数据库连接池(37页珍藏版)》请在金锄头文库上搜索。
1、- 1 -相关技术:连接池 引用记数 多线程 C#.Net Java 适宜人群数据库应用程序程序员 系统分析员 模块设计师 有一定功底的程序员 目录引言 数据库连接池(Connection Pool)的工作原理 连接池关键问题分析 并发问题 事务处理 连接池的分配与释放 连接池的配置与维护 关键议题引用记数如何实现事务处理管理连接池结合代码说明构造方法启动服务 StartService停止服务 StopService申请 GetConnectionFormPool释放 DisposeConnection如何更新属性如何确定连接是否失效使用线程管理连接池threadCreatethreadChe
2、ck其他-引言- 2 -一般的数据库应用程序大致都遵循下面的步骤:初始化程序用户在 UI 上输入操作由用户操作产生数据库操作将数据库操作递交到数据库服务器.(重复 24)关闭应用程序而本文则着重讲解上面第 4 步骤.在着一步骤中我们经常是,打开数据库连接操作数据库,最后关闭数据库.在服务器端程序设计上与数据库的操作显得十分重要,因为你要处理的数据操作十分巨大.如果频繁创建数据库连接频繁关闭数据库连接则会引起效率低下甚至引发程序崩溃.也许我们可以有另一种操作数据库的形式,我们可以在程序运行时打开一个数据库连接,让这个连接永久存在直到程序死亡,那么这样做也有不安全隐患,我们知道一个对象存在时间越长
3、或被使用次数越多则它表现的越不稳定,这着不稳定因素是由于对象内部可能存在的潜在设计问题产生,对于数据库连接对象道理也一样.我们不能保证一个 Connection 对象里面能一点问题不存在.所以我们也不敢长时间将它长时间占用内存.既然有这么多的问题由此我们需要一个能帮我们维护数据库连接的东西-它就是连接池,网上有很多的连接池例子,但是多数都是简单的例子,或者介绍比较复杂的连接池原理,没有一个比较完整介绍和实现连接池的例子.这里就介绍你如何自己制作一个连接池.对于共享资源,有一个很著名的设计模式:资源池(Resource Pool) 。该模式正是为了解决资源的频繁分配释放所造成的问题。为解决我们的
4、问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池” 。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量使用情况,为系统开发测试及性能调整提供依据。连接池的基本工作原理见下图。数据库连接池(Connection Pool)的工作原理连接池的关键问题分析1、并发问题为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对
5、并发管理的支持像 java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考,相关文献。、事务处理我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组 SQL 语句要么全做,要么全不做。我们知道当个线程公用一个连接 Connection 对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使 Connection 类提供了相应的事务支持,- 3 -可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有个线程都在进行事务操作而引起的。为此我们可以使用
6、每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。、连接池的分配与释放连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。对于连接的管理可使用一个 List。即把已经创建的连接都放入 List 中去统一管理。每当用户请求一个连接时,系统检查这个 List 中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出) ;如果没有就抛出一个异常给用户,List 中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程
7、的具体实现。 、连接池的配置与维护连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。如何确保连接池中的
8、最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。 关键议题引用记数在分配、释放策略对于有效复用连接非常重要,我们采用的方法也是采用了一个很有名的设计模式:Reference Counting(引用记数) 。该模式在复用资源方面用的非常广泛,我们把该方法运用到对于连接的分配释放上。每一个数据库连接,保留一个引用记数,用来记录该连接的使用者的个数。具体的实现上,我们对 Connection 类进行进一步包装来实现引用记数。被包装的 Connection 类我们
9、提供个方法来实现引用记数的操作,一个是Repeat(被分配出去)Remove(被释放回来) ;然后利用 RepeatNow 属性来确定当前被引用多少,具体是哪个用户引用了该连接将在连接池中登记;最后提供 IsRepeat 属性来确定该连接是否可以使用引用记数技术。一旦一个连接被分配出去,那么就会对该连接的申请者进行登记,并且增加引用记数,当被释放回来时候就删除他已经登记的信息,同时减少- 4 -一次引用记数。这样做有一个很大的好处,使得我们可以高效的使用连接,因为一旦所有连接都被分配出去,我们就可以根据相应的策略从使用池中挑选出一个已经正在使用的连接用来复用,而不是随意拿出一个连接去复用。策略
10、可以根据需要去选择,我们有策略可是使用:.ConnLevel_ReadOnly独占方式使用空闲的实际连接分配连接资源,并且在该资源释放回之前,该资源在连接池中将不能将其引用分配给其他申请者。如果连接池中所有实际连接资源都已经分配出去,那么即使连接池可以在分配引用资源在该模式下连接池将不会分配连接资源,连接池会产生一个异常,标志连接池资源耗尽。例:假如一个实际连接可以被分配 5 次,那么使用该模式申请连接的话您将损失 4 个可分配的连接,只将得到一个连接资源。 直至该资源被释放回连接池,连接池才继续分配它剩余的 4 次机会。 当你在使用连接时可能应用到事务时,可以使用该模式的连接,以确定在事务进
11、行期间您可以对该连接具有独享权限,以避免各个数据库操作访问的干扰。.ConnLevel_High优先级-高使用空闲的实际连接分配连接资源,并且在该资源释放回之前,该资源在连接池中将可能将其引用分配给其他申请者。*注意:此级别不保证在分配该资源后,仍然保持独立占有连接资源,若想独立占有资源请使用 ReadOnely, 因为当连接池达到某一时机时该资源将被重复分配(引用记数)然而这个时机是不可预测的。如果您申请的连接会用于事务处理您可以使用 ConnLevel_ReadOnly 级别。.ConnLevel_None 优先级-中适当应用引用记数技术分配连接资源。在该模式下,连接池内部会按照实际连接已
12、经使用次数排序(多-少) ,然后在结果中选取1/3 位置的连接资源返回。与优先级 -高相同该模式也不具备保持独立占有连接资源的特性。如果您申请的连接会用于事务处理您可以使用 ConnLevel_ReadOnly 级别。.ConnLevel_Bottom 优先级-底尽可能使用引用记数技术分配连接。在该模式下,连接池内部会按照实际连接已经使用次数排序(多-少),然后在结果中选取被使用最多的返回。该模式适合处理较为不重要的连接资源请求。与优先级- 高相同该模式也不具备保持独立占有连接资源的特性。如果您申请的连接会用于事务处理您可以使用 ConnLevel_ReadOnly 级别。以上 4 条策略选自
13、 datebasepool_SDK(datebasepool 是本文所开发的最终产物)如何实现事务处理前面谈到的都是关于使用数据库连接进行普通的数据库访问。对于事务处理,情况就变得比较复杂。因为事务本身要求原子性的保证,此时就要求对于数据库的操作符合All-All-Nothing原则, 即要么全部完成,要么什么都不做。如果简单的采用上述的连接复用的策略,就会发生问题,因为没有办法控制属于同一个事务的多个数据库操作方法的动作,可能这些数据库操作是在多个连接上进行的,并且这些连接可能被其他非事务方法复用。- 5 -Connection 本身具有提供了对于事务的支持,具体实现方法请参看 Connec
14、tion 的msdn,显式的调用 commit 或者 rollback 方法来实现。但是要安全、高效的进行 Connection进行复用,就必须提供相应的事务支持机制。我们采用的方法是:用户以ConnLevel_ReadOnly 模式申请一个连接之后该连接就由该申请者独自享有该连接,具体事务操作由用户自行设计编写,连接池只提供用户独自占有。管理连接池在上文中我们说过这个连接池内部连接管理使用的是独立的线程来工作(threadCreate和 threadCheck)threadCreate 线程负责创建连接 ,threadCheck 线程负责检查每个连接是否达到自己的寿命,标志连接寿命的条件是被
15、引用的次数超过它最大被引用次数,或者达到最大生存时间。这些参数都由 ConnStruct 类管理,ConnStruct 类是包装连接的类,下面定义的就是连接池使用到的属性变量(C#代码)/属性private int _realFormPool;/连接池中存在的实际连接数(包含失效的连接)private int _potentRealFormPool;/连接池中存在的实际连接数( 有效的实际连接)private int _spareRealFormPool;/空闲的实际连接private int _useRealFormPool;/已分配的实际连接private int _readOnlyFor
16、mPool;/连接池已经分配多少只读连接private int _useFormPool;/已经分配出去的连接数private int _spareFormPool;/目前可以提供的连接数private int _maxConnection;/最大连接数,最大可以创建的连接数目private int _minConnection;/最小连接数private int _seepConnection;/每次创建连接的连接数private int _keepRealConnection;/保留的实际空闲连接,以备可能出现的 ReadOnly使用,当空闲连接不足该数值时,连接池将创建 seepConnection 个连接private int _exist = 20;/每个连接生存期限 20 分钟private int