Java数据库连接池详解

时间:2021-09-20 06:17:39

http://www.javaweb1024.com/java/JavaWebzhongji/2015/06/01/736.html

对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决我们的问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。

为什么使用连接池

连接,是我们的编程语言与数据库交互的一种方式。我们经常会听到这么一句话“数据库连接很昂贵“。

有人接受这种说法,却不知道它的真正含义。因此,下面通过实例解释它究竟是什么。

下面是Mysql数据库创建连接的的一段代码:

[java] view plaincopyprint?

 
 
  1. String connUrl ="jdbc:mysql://your.database.domain/yourDBname";

  2. Class.forName("com.mysql.jdbc.Driver");

  3. Connection con =DriverManager.getConnection (connUrl);

当我们创建了一个Connection对象,它在内部都执行了什么:

1.“DriverManager”检查并注册驱动程序;

2.“com.mysql.jdbc.Driver”就是我们注册了的驱动程序,它会在驱动程序类中调用“connect(url…)”方法。

3.com.mysql.jdbc.Driver的connect方法根据我们请求的“connUrl”,创建一个“Socket连接”,连接到IP为“your.database.domain”,默认端口3306的数据库。

4.创建的Socket连接将被用来查询我们指定的数据库,并最终让程序返回得到一个结果。

简单的获取一个连接,系统却要在背后做很多消耗资源的事情,大多时候,创建连接的时间比执行sql语句的时间还要长。

传统的获取连接方式如下图所示:

Java数据库连接池详解

用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。

采用连接池技术后的过程如下:

Java数据库连接池详解

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

需要注意的问题

1、并发问题

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

2、事务处理

我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。

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

3、连接池的分配与释放

连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。

对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理。

4、连接池的配置与维护

连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。

如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。

Tomcat连接池

Tomcat默认使用的是DBCP数据库连接池,其实从本质上讲,Tomcat是利用Apache Commons DBCP来实现的,只不过把特定的功能集成到了tomcat-dbcp.jar包中。

使用法法如下:

步骤1:

在Tomcat中Context.xml中添加

[html] view plaincopyprint?

 
 
  1. <!--  path表示站点的访问方式 -->

  2. <!--    例:http://localhost:8080/test 配置为/test -->

  3. <!-- docBase="fileLocation" 应用存储的实际路径,没有的话则从webapps目录找 -->

  4. <!-- Context标签内的这些属性都可以省略不写,使用默认的设置 -->

  5. <Context path="/TomcatDbPools" docBase="TomcatDbPools" debug="0" reloadable="true">

  6. <!--     使用DBCP配置的数据源        -->

  7. <Resource

  8. <!-- 指定资源池的Resource的JNDI的名字,就是给连接池起的名字 -->

  9. name="jdbc/mysql_connect"

  10. <!-- 管理权限,指定管理Resource的Manager,可以是Container或Application -->

  11. auth="Container"

  12. <!--指出Resource所属的类名,是什么类型的数据源-->

  13. type="javax.sql.DataSource"

  14. <!-- 数据库驱动类 -->

  15. driverClassName="com.mysql.jdbc.Driver"

  16. <!-- 数据库连接url-->

  17. url=" jdbc:mysql://localhost:3306/test"

  18. <!-- 数据库用户名 -->

  19. username="admin"

  20. <!-- 数据库密码 -->

  21. password="123456"

  22. <!-- 连接池最大激活的连接数,设为0表示无限制-->

  23. maxActive="100"

  24. <!-- 连接池中最多可空闲的连接数 -->

  25. maxIdle="30"

  26. <!-- 为连接最大的等待时间,单位毫秒,如果超过此时间将接到异常。设为-1表示无限制-->

  27. maxWait="10000" />

  28. </context>

注:还可以用minIdle配置连接池中最少空闲maxIdle个连接,用initialSize配置初始化连接数目。可同时配置多个数据源。

如果在Tomcat的server.xml文件中配置数据源,有两种方法都可以实现:

方法1:将上面的配置内容直接添加在<Host>节点下。

方法2:在<GlobalNamingResources>节点下添加:

[html] view plaincopyprint?

 
 
  1. <GlobalNamingResources>

  2. <!-- 这里的factory指的是该Resource 配置使用的是哪个数据源配置类,这里使用的是tomcat自带的标准数据源Resource配置类,-->

  3. <!-- 这个类也可以自己写,实现javax.naming.spi.ObjectFactory 接口即可。 -->

  4. <!-- 某些地方使用的commons-dbcp.jar中的org.apache.commons.dbcp.BasicDataSourceFactory,-->

  5. <!-- 如果使用这个就需把commons-dbcp.jar及其依赖的jar包,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不够的。  -->

  6. <Resource

  7. name="mysql_connect"

  8. factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"

  9. maxActive="100"

  10. maxIdle="30"

  11. maxWait="10000"

  12. name="jdbc/TomcatDbPool1"

  13. password="123456"

  14. type="javax.sql.DataSource"

  15. url="jdbc:mysql://localhost:3306/test"

  16. username="root"/>

  17. </GlobalNamingResources>

然后在context.xml文件中的<Context></Context>节点中加入如下内容:

[html] view plaincopyprint?

 
 
  1. <ResourceLink name="jdbc/mysql_connect" global="mysql_connect" type="javax.sql.DataSource"/>

在server.xml中配置的数据源是全局的,所有项目都可以使用。全局的resource只是为了重用,方便所有该tomcat下的web工程的数据源管理,但如果你的tomcat不会同时加载多个web工程,也就是说一个tomcat只加载一个web工程时,是没有必要配置全局的resource的。

此外,还需要将mysql的Java驱动类以及其他依赖包(如果有)放到tomcat的lib目录下。

步骤2:

在web.xml中,配置<resource-ref>元素以在web应用中引用JNDI资源。

[html] view plaincopyprint?

 
 
  1. <resource-ref>

  2. <!-- 对该资源的描述语言 -->

  3. <description> dbcpconnect</description>

  4. <!-- 引用的资源名,必须与Context.xml中的名字一致 -->

  5. <res-ref-name> jdbc/mysql_connect </res-ref-name>

  6. <!-- 资源类型 -->

  7. <res-type>javax.sql.DataSource</res-type>

  8. <!-- 管理权限 -->

  9. <res-auth>Container</res-auth>

  10. </resource-ref>

步骤3:

在Web应用中使用数据源

[java] view plaincopyprint?

 
 
  1. //获得对数据源的引用:

  2. Context ctx =new InitialContext();

  3. //java:comp/env/是java中JNDI固定写法。

  4. DataSource ds =(DataSource) ctx.lookup("java:comp/env/jdbc/mysql_connect ");

  5. //获得数据库连接对象:

  6. Connection conn= ds.getConnection();

  7. //返回数据库连接到连接池:

  8. conn.close();

DBCP连接池

DBCP 是 Apache 软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个 jar 文件:

Commons-dbcp.jar:连接池的实现

Commons-pool.jar:连接池实现的依赖库

Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

步骤1:

在类目录下加入dbcp的配置文件:dbcp.properties

[plain] view plaincopyprint?Java数据库连接池详解Java数据库连接池详解

 
 
  1. #数据库驱动

  2. driverClassName=com.mysql.jdbc.Driver

  3. #数据库连接地址

  4. url=jdbc:mysql://localhost/test

  5. #用户名

  6. username=root

  7. #密码

  8. password=123456

  9. #连接池的最大数据库连接数。设为0表示无限制

  10. maxActive=30

  11. #最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连

  12. #接将被标记为不可用,然后被释放。设为0表示无限制

  13. maxIdle=10

  14. #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制

  15. maxWait=1000

  16. #超过removeAbandonedTimeout时间后,是否进行没用连接(废弃)的回收(默认为false,调整为true)

  17. removeAbandoned=true

  18. #超过时间限制,回收没有用(废弃)的连接(默认为 300秒)

  19. removeAbandonedTimeout=180

步骤2:

在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池:

[java] view plaincopyprint?

 
 
  1. import java.io.InputStream;

  2. import java.sql.Connection;

  3. import java.sql.ResultSet;

  4. import java.sql.SQLException;

  5. import java.sql.Statement;

  6. import java.util.Properties;

  7. import javax.sql.DataSource;

  8. import org.apache.commons.dbcp.BasicDataSourceFactory;

  9. /**

  10. * 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现

  11. * DBCP连接池就是java.sql.DataSource接口的一个具体实现

  12. */

  13. public classJdbcUtils_DBCP {

  14. private static DataSource ds = null;

  15. //在静态代码块中创建数据库连接池

  16. static{

  17. try{

  18. //加载dbcp.properties配置文件

  19. InputStream in =JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties");

  20. Properties prop = new Properties();

  21. prop.load(in);

  22. //创建数据源

  23. ds =BasicDataSourceFactory.createDataSource(prop);

  24. }catch (Exception e) {

  25. throw newExceptionInInitializerError(e);

  26. }

  27. }

  28. //从数据源中获取数据库连接

  29. public static Connection getConnection()throws SQLException{

  30. //从数据源中获取数据库连接

  31. return ds.getConnection();

  32. }

  33. //释放连接

  34. public static void release(Connection conn){

  35. if(conn!=null){

  36. try{

  37. //将Connection连接对象还给数据库连接池

  38. conn.close();

  39. }catch (Exception e) {

  40. e.printStackTrace();

  41. }

  42. }

  43. }

  44. }

步骤3:

在应用中获取连接

[java] view plaincopyprint?

 
 
  1. Connection conn = null;

  2. PreparedStatement st = null;

  3. ResultSet rs = null;

  4. try{

  5. //获取数据库连接

  6. conn =JdbcUtils_DBCP.getConnection();

  7. ……

  8. }catch (Exception e) {

  9. e.printStackTrace();

  10. }finally{

  11. //释放资源

  12. JdbcUtils_DBCP.release(conn);

  13. }

C3P0连接池

c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。c3p0一般是与Hibernate,Spring等框架一块使用的,当然也可以单独使用。

dbcp没有自动回收空闲连接的功能,c3p0有自动回收空闲连接功能。

使用c3p0需要导入c3p0.jar、mchange-commons-.jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-pre1.jar。

步骤1:

在类目录下加入C3P0的配置文件:c3p0-config.xml

[html] view plaincopyprint?

 
 
  1. <c3p0-config>

  2. <!--  C3P0的缺省(默认)配置,-->

  3. <!-- 如果在代码中“ComboPooledDataSourceds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源 -->

  4. <default-config>

  5. <property name="driverClass">com.mysql.jdbc.Driver</property>

  6. <property name="jdbcUrl">jdbc:mysql://localhost:3306/anysearch</property>

  7. <property name="user">root</property>

  8. <property name="password">123456</property>

  9. <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default:3 -->

  10. <property name="acquireIncrement">5</property>

  11. <!--初始化的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3-->

  12. <property name="initialPoolSize">10</property>

  13. <!--连接池中保留的最小连接数-->

  14. <property name="minPoolSize">5</property>

  15. <!--连接池中保留的最大连接数。Default:15 -->

  16. <property name="maxPoolSize">20</property>

  17. <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->

  18. <property name="acquireRetryAttempts">30</property>

  19. <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->

  20. <property name="acquireRetryDelay">1000</property>

  21. <!--连接关闭时默认将所有未提交的操作回滚。Default: false -->

  22. <property name="autoCommitOnClose">false</property>

  23. </default-config>

  24. <!-- C3P0的命名配置,-->

  25. <!-- 如果在代码中“ComboPooledDataSourceds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源 -->

  26. <named-config name="MySQL">

  27. <property name="driverClass">com.mysql.jdbc.Driver</property>

  28. <property name="jdbcUrl">jdbc:mysql://localhost:3306/test2</property>

  29. <property name="user">root</property>

  30. <property name="password">123456</property>

  31. <property name="acquireIncrement">5</property>

  32. <property name="initialPoolSize">10</property>

  33. <property name="minPoolSize">5</property>

  34. <property name="maxPoolSize">20</property>

  35. </named-config>

  36. </c3p0-config>

还有更多可设置的参数,具体可查阅相关资料。

步骤2:

在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池

[java] view plaincopyprint?

 
 
  1. import java.sql.Connection;

  2. import java.sql.ResultSet;

  3. import java.sql.SQLException;

  4. import java.sql.Statement;

  5. import com.mchange.v2.c3p0.ComboPooledDataSource;

  6. public class JdbcUtils_C3P0 {

  7. private static ComboPooledDataSource ds =null;

  8. //在静态代码块中创建数据库连接池

  9. static{

  10. try{

  11. //通过代码创建C3P0数据库连接池

  12. /*ds = new ComboPooledDataSource();

  13. ds.setDriverClass("com.mysql.jdbc.Driver");

  14. ds.setJdbcUrl("jdbc:mysql://localhost:3306/test");

  15. ds.setUser("root");

  16. ds.setPassword("123456");

  17. ds.setInitialPoolSize(10);

  18. ds.setMinPoolSize(5);

  19. ds.setMaxPoolSize(20);*/

  20. //通过读取C3P0的xml配置文件创建数据源,C3P0的xml配置文件c3p0-config.xml必须放在src目录下

  21. //ds = newComboPooledDataSource();//使用C3P0的默认配置来创建数据源

  22. ds = newComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源

  23. }catch (Exception e) {

  24. throw newExceptionInInitializerError(e);

  25. }

  26. }

  27. //从数据源中获取数据库连接

  28. public static Connection getConnection()throws SQLException{

  29. //从数据源中获取数据库连接

  30. return ds.getConnection();

  31. }

  32. //释放链接

  33. public static void release(Connection conn){

  34. if(conn!=null){

  35. try{

  36. //将Connection连接对象还给数据库连接池

  37. conn.close();

  38. }catch (Exception e) {

  39. e.printStackTrace();

  40. }

  41. }

  42. }

  43. }

步骤3:

在应用中获取连接

[java] view plaincopyprint?Java数据库连接池详解Java数据库连接池详解

 
 
  1. Connection conn = null;

  2. PreparedStatement st = null;

  3. ResultSet rs = null;

  4. try{

  5. //获取数据库连接

  6. conn = JdbcUtils_C3P0.getConnection();

  7. ……

  8. }catch (Exception e) {

  9. e.printStackTrace();

  10. }finally{

  11. //释放资源

  12. JdbcUtils_C3P0release(conn);

  13. }

其他连接池

此外,还有其他的连接池可供选择,比如使用比较广泛的Proxool。Proxool是一种Java数据库连接池技术。是sourceforge下的一个开源项目,这个项目提供一个健壮、易用的连接池,最为关键的是这个连接池提供监控的功能,方便易用,便于发现连接泄漏的情况。

proxool和 c3p0能够更好的支持高并发,但是在稳定性方面略逊于dpcp。

可根据项目的实际需要来选择连接池。

Java数据库连接池详解的更多相关文章

  1. Java线程池详解(二)

    一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...

  2. Java连接池详解

    于共享资源,有一个很著名的设计模式:资源池(Resource Pool).该模式正是为了解决资源的频繁分配﹑释放所造成的问题.为解决我们的问题,可以采用数据库连接池技术.数据库连接池的基本思想就是为数 ...

  3. Java线程池详解

    一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...

  4. 【java线程系列】java线程系列之java线程池详解

    一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在 ...

  5. Java线程池详解,看这篇就够了!

    构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础知识 Execu ...

  6. Java线程池 详解(图解)

    来源:www.jianshu.com/p/098819be088c 拓展: 手动创建 new ThreadPoolExecutor 的使用: https://segmentfault.com/a/11 ...

  7. Java 线程池详解

    Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但 ...

  8. Java线程池详解(一)

    一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...

  9. 【转】Java常量池详解

    今天My partner问我一个让他头疼的Java question,求输出结果: /** * * @author DreamSea 2011-11-19 */ public class Intege ...

随机推荐

  1. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之三

    五.睡眠和唤醒一个线程 有时,你会想要在一段特定的时间后再去中断线程的运行.举个例子,程序中的一个线程每一分钟检查一次传感器的状态,剩余的时间,线程应该处于空闲的状态.在这段空闲时间里,线程不会使用计 ...

  2. Hadoop TextInputFormat源码分析

    from:http://blog.csdn.net/lzm1340458776/article/details/42707047 InputFormat主要用于描述输入数据的格式(我们只分析新API, ...

  3. CentOS 7&period;3&sol;Linux &period;net core sdk 安装

    执行下列命令,安装.NET Core SDK(微软官方教程地址 https://www.microsoft.com/net/learn/get-started/linuxcentos) 点开链接,选择 ...

  4. ReSharper导致Visual Studio缓慢?

    问题排查 我们会竭尽所能的ReSharper的性能方面,但是也有一些已知和未知的情况下,ReSharper的可以减缓的Visual Studio. 这里有一些关键点进行故障排除和修复ReSharper ...

  5. nginx的rewrite ,如何在flask项目中获取重写前的url

    1. 在flask配一个重写到哪的路由,假设是/rewite/,然后到nginx的配置文件写重写规则,我这里重写全部的请求,接着测试能否重写成功 1. 添加一个路由 配置重写规则 测试成功 2.接下来 ...

  6. 如何查看路由器的mac和计算机的mac

    如何查看路由器的mac和计算机的mac 一.查看路由器的mac 方法一: 直接看路由器的背面,如下图,即可看到MAC地址   打开命令提示符窗口,输入ipconfig,找到网关地址,如192.168. ...

  7. jstorm知识整理

    最近在做一个jstorm的程序.我的jstorm程序消费一个kafka主题,根据数据逻辑判断需要往下游哪几个kafka主题的生产者发送. 1.bolt的execute(Tuple input)方法每次 ...

  8. 【转载】R中有关数据挖掘的包

    下面列出了可用于数据挖掘的R包和函数的集合.其中一些不是专门为了数据挖掘而开发,但数据挖掘过程中这些包能帮我们不少忙,所以也包含进来. 1.聚类 常用的包: fpc,cluster,pvclust,m ...

  9. 安装与配置JDK

    第一步:下载jdk-7-linux-i586.tar.gzwget -c http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-i586. ...

  10. 数据结构---python---表

    一.list的基本实现技术 在数据结构中,如果用python实现线性表,无疑要提到list,list是一种元素个数可变的线性表(而tuple是不变的表,不支持改变其内部状态的任何操作,其他与list性 ...