数据库连接池

一、什么是数据库连接池?

官方:数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
个人理解:创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。

二、数据库连接池的运行机制

(1)  程序初始化时创建连接池
(2) 使用时向连接池申请可用连接
(3) 使用完毕,将连接返还给连接池
(4) 程序退出时,断开所有连接,并释放资源

三、数据库连接池的使用

作为开源的数据库连接池,C3P0是一个优秀的连接池,性能也十分可靠。

首先到http://sourceforge.net/projects/c3p0/下载相应的jar包,总共三个,如下图所示。


其次将jar包导入到工程当中,然后就可以使用cp30了。
示例代码如下:
[java]  view plain  copy
  1. package com.zww.server;  
  2.   
  3. import java.beans.PropertyVetoException;  
  4. import java.sql.Connection;  
  5. import java.sql.SQLException;  
  6. import com.mchange.v2.c3p0.ComboPooledDataSource;  
  7.   
  8. public final class ConnectionManager {  
  9.     //使用单利模式创建数据库连接池  
  10.     private static ConnectionManager instance;  
  11.     private static ComboPooledDataSource dataSource;  
  12.   
  13.     private ConnectionManager() throws SQLException, PropertyVetoException {  
  14.         dataSource = new ComboPooledDataSource();  
  15.   
  16.         dataSource.setUser("root");     //用户名  
  17.         dataSource.setPassword("123456"); //密码  
  18.         dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/zww");//数据库地址  
  19.         dataSource.setDriverClass("com.mysql.jdbc.Driver");  
  20.         dataSource.setInitialPoolSize(5); //初始化连接数  
  21.         dataSource.setMinPoolSize(1);//最小连接数  
  22.         dataSource.setMaxPoolSize(10);//最大连接数  
  23.         dataSource.setMaxStatements(50);//最长等待时间  
  24.         dataSource.setMaxIdleTime(60);//最大空闲时间,单位毫秒  
  25.     }  
  26.   
  27.     public static final ConnectionManager getInstance() {  
  28.         if (instance == null) {  
  29.             try {  
  30.                 instance = new ConnectionManager();  
  31.             } catch (Exception e) {  
  32.                 e.printStackTrace();  
  33.             }  
  34.         }  
  35.         return instance;  
  36.     }  
  37.   
  38.     public synchronized final Connection getConnection() {  
  39.         Connection conn = null;  
  40.         try {  
  41.             conn = dataSource.getConnection();  
  42.         } catch (SQLException e) {  
  43.             e.printStackTrace();  
  44.         }  
  45.         return conn;  
  46.     }  
  47. }  
下面是测试代码:
[java]  view plain  copy
  1. package com.zww.server;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.PreparedStatement;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7.   
  8. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;  
  9.   
  10.   
  11. public class ConnectionDemo {  
  12.   
  13.     public static void main(String[] args) throws SQLException {  
  14.         System.out.println("使用连接池................................");  
  15.         for (int i = 0; i < 20; i++) {  
  16.             long beginTime = System.currentTimeMillis();  
  17.             Connection conn = ConnectionManager.getInstance().getConnection();  
  18.             try {  
  19.                 PreparedStatement pstmt = conn.prepareStatement("select * from event");  
  20.                 ResultSet rs = pstmt.executeQuery();  
  21.                 while (rs.next()) {  
  22.                      // do nothing...  
  23.                 }  
  24.             } catch (SQLException e) {  
  25.                 e.printStackTrace();  
  26.             } finally {  
  27.                 try {  
  28.                     conn.close();  
  29.                 } catch (SQLException e) {  
  30.                     e.printStackTrace();  
  31.                 }  
  32.             }  
  33.   
  34.             long endTime = System.currentTimeMillis();  
  35.             System.out.println("第" + (i + 1) + "次执行花费时间为:" + (endTime - beginTime));  
  36.         }  
  37.   
  38.         System.out.println("不使用连接池................................");  
  39.         for (int i = 0; i < 20; i++) {  
  40.             long beginTime = System.currentTimeMillis();  
  41.             MysqlDataSource mds = new MysqlDataSource();  
  42.             mds.setURL("jdbc:mysql://localhost:3306/zww");  
  43.             mds.setUser("root");  
  44.             mds.setPassword("123456");  
  45.             Connection conn = mds.getConnection();  
  46.             try {  
  47.                 PreparedStatement pstmt = conn.prepareStatement("select * from event");  
  48.                 ResultSet rs = pstmt.executeQuery();  
  49.                 while (rs.next()) {  
  50.                                     // do nothing...  
  51.                 }  
  52.             } catch (SQLException e) {  
  53.                 e.printStackTrace();  
  54.             } finally {  
  55.                 try {  
  56.                     conn.close();  
  57.                 } catch (SQLException e) {  
  58.                     e.printStackTrace();  
  59.                 }  
  60.             }  
  61.             long endTime = System.currentTimeMillis();  
  62.             System.out.println("第" + (i + 1) + "次执行花费时间为:"  
  63.                                 + (endTime - beginTime));  
  64.         }  
  65.   
  66.     }  

运行结果如下图所示:

测试结果表明,在使用连接池时,只在第一次初始化时,比较耗时,完成初始化之后,使用连接池进行数据库操作明显比不使用连接池花费的时间少。


一. 为何要使用数据库连接池
假设网站一天有很大的访问量,数据库服务器就需要为每次连接创建一次数据库连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.

      数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:

  1, 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
  2, 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
  3, 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被           放到连接池中等待重复使用或是空间超时后被释放.

二、使用数据库连接池的关键点

1、并发问题

  为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考,相关文献。

2、事务处理

DB连接池必须要确保某一时间内一个 conn 只能分配给一个线程。不同 conn 的事务是相互独立的。 

  我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。 
  我们知道当2个线程共用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。 

 

3、连接池的分配与释放

  连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。 
  对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。

4、连接池的配置与维护

  连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。 
  如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。

 

三、使用数据库连接池的优势和其工作原理

1、连接池的优势

连接池用于创建和管理数据库连接的缓冲池技术,缓冲池中的连接可以被任何需要他们的线程使用。当一个线程需要用JDBC对一个数据库操作时,将从池中请求一个连接。当这个连接使用完毕后,将返回到连接池中,等待为其他的线程服务。

连接池的主要优点有以下三个方面。

第一、减少连接创建时间。连接池中的连接是已准备好的、可重复使用的,获取后可以直接访问数据库,因此减少了连接创建的次数和时间。

第二、简化的编程模式。当使用连接池时,每一个单独的线程能够像创建一个自己的JDBC连接一样操作,允许用户直接使用JDBC编程技术。

第三、控制资源的使用。如果不使用连接池,每次访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了系统在大量用户应用时的稳定性。

2、连接池的工作原理

下面,简单的阐述下连接池的工作原理。

连接池技术的核心思想是连接复用,通过建立一个数据库连接池以及一套连接使用、分配和管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。

连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。

第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。

第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

  • 当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
  • 当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。

    该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。

 

程序开发过程中,存在很多问题:

首先,每一次web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。

可是对于现在的web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库

     通过上面的分析,我们可以看出来,“数据库连接”是一种稀缺的资源,为了保障网站的正常使用,应该对其进行妥善管理。其实我们查询完数据库后,如果不关闭连接,而是暂时存放起来,当别人使用时,把这个连接给他们使用。就避免了一次建立数据库连接和断开的操作时间消耗。

数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接

创建数据库连接池大概有3个步骤:

① 创建ConnectionPool实例,并初始化创建10个连接,保存在Vector中(线程安全)单例模式实现
② 实现getConnection()从连接库中获取一个可用的连接
③ returnConnection(conn) 提供将连接放回连接池中方法



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值