Java连接池详解
于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决我们的问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。
为什么使用连接池
连接,是我们的编程语言与数据库交互的一种方式。我们经常会听到这么一句话“数据库连接很昂贵“。
有人接受这种说法,却不知道它的真正含义。因此,下面通过实例解释它究竟是什么。
下面是Mysql数据库创建连接的的一段代码:
[java] view plaincopyprint?
- String connUrl ="jdbc:mysql://your.database.domain/yourDBname"; 
- Class.forName("com.mysql.jdbc.Driver"); 
- 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语句的时间还要长。
传统的获取连接方式如下图所示:
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
采用连接池技术后的过程如下:
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
需要注意的问题
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?
- <!-- path表示站点的访问方式 --> 
- <!-- 例:http://localhost:8080/test 配置为/test --> 
- <!-- docBase="fileLocation" 应用存储的实际路径,没有的话则从webapps目录找 --> 
- <!-- Context标签内的这些属性都可以省略不写,使用默认的设置 --> 
- <Context path="/TomcatDbPools" docBase="TomcatDbPools" debug="0" reloadable="true"> 
- <!-- 使用DBCP配置的数据源 --> 
- <Resource 
- <!-- 指定资源池的Resource的JNDI的名字,就是给连接池起的名字 --> 
- name="jdbc/mysql_connect" 
- <!-- 管理权限,指定管理Resource的Manager,可以是Container或Application --> 
- auth="Container" 
- <!--指出Resource所属的类名,是什么类型的数据源--> 
- type="javax.sql.DataSource" 
- <!-- 数据库驱动类 --> 
- driverClassName="com.mysql.jdbc.Driver" 
- <!-- 数据库连接url--> 
- url=" jdbc:mysql://localhost:3306/test" 
- <!-- 数据库用户名 --> 
- username="admin" 
- <!-- 数据库密码 --> 
- password="123456" 
- <!-- 连接池最大激活的连接数,设为0表示无限制--> 
- maxActive="100" 
- <!-- 连接池中最多可空闲的连接数 --> 
- maxIdle="30" 
- <!-- 为连接最大的等待时间,单位毫秒,如果超过此时间将接到异常。设为-1表示无限制--> 
- maxWait="10000" /> 
- </context> 
注:还可以用minIdle配置连接池中最少空闲maxIdle个连接,用initialSize配置初始化连接数目。可同时配置多个数据源。
如果在Tomcat的server.xml文件中配置数据源,有两种方法都可以实现:
方法1:将上面的配置内容直接添加在<Host>节点下。
方法2:在<GlobalNamingResources>节点下添加:
[html] view plaincopyprint?
- <GlobalNamingResources> 
- <!-- 这里的factory指的是该Resource 配置使用的是哪个数据源配置类,这里使用的是tomcat自带的标准数据源Resource配置类,--> 
- <!-- 这个类也可以自己写,实现javax.naming.spi.ObjectFactory 接口即可。 --> 
- <!-- 某些地方使用的commons-dbcp.jar中的org.apache.commons.dbcp.BasicDataSourceFactory,--> 
- <!-- 如果使用这个就需把commons-dbcp.jar及其依赖的jar包,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不够的。 --> 
- <Resource 
- name="mysql_connect" 
- factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" 
- maxActive="100" 
- maxIdle="30" 
- maxWait="10000" 
- name="jdbc/TomcatDbPool1" 
- password="123456" 
- type="javax.sql.DataSource" 
- url="jdbc:mysql://localhost:3306/test" 
- username="root"/> 
- </GlobalNamingResources> 
然后在context.xml文件中的<Context></Context>节点中加入如下内容:
[html] view plaincopyprint?
- <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?
- <resource-ref> 
- <!-- 对该资源的描述语言 --> 
- <description> dbcpconnect</description> 
- <!-- 引用的资源名,必须与Context.xml中的名字一致 --> 
- <res-ref-name> jdbc/mysql_connect </res-ref-name> 
- <!-- 资源类型 --> 
- <res-type>javax.sql.DataSource</res-type> 
- <!-- 管理权限 --> 
- <res-auth>Container</res-auth> 
- </resource-ref> 
步骤3:
在Web应用中使用数据源
[java] view plaincopyprint?
- //获得对数据源的引用: 
- Context ctx =new InitialContext(); 
- //java:comp/env/是java中JNDI固定写法。 
- DataSource ds =(DataSource) ctx.lookup("java:comp/env/jdbc/mysql_connect "); 
- //获得数据库连接对象: 
- Connection conn= ds.getConnection(); 
- //返回数据库连接到连接池: 
- conn.close(); 
DBCP连接池
DBCP 是 Apache 软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
步骤1:
在类目录下加入dbcp的配置文件:dbcp.properties
[plain] view plaincopyprint?
- #数据库驱动 
- driverClassName=com.mysql.jdbc.Driver 
- #数据库连接地址 
- url=jdbc:mysql://localhost/test 
- #用户名 
- username=root 
- #密码 
- password=123456 
- #连接池的最大数据库连接数。设为0表示无限制 
- maxActive=30 
- #最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连 
- #接将被标记为不可用,然后被释放。设为0表示无限制 
- maxIdle=10 
- #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制 
- maxWait=1000 
- #超过removeAbandonedTimeout时间后,是否进行没用连接(废弃)的回收(默认为false,调整为true) 
- removeAbandoned=true 
- #超过时间限制,回收没有用(废弃)的连接(默认为 300秒) 
- removeAbandonedTimeout=180 
步骤2:
在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池:
[java] view plaincopyprint?
- import java.io.InputStream; 
- import java.sql.Connection; 
- import java.sql.ResultSet; 
- import java.sql.SQLException; 
- import java.sql.Statement; 
- import java.util.Properties; 
- import javax.sql.DataSource; 
- import org.apache.commons.dbcp.BasicDataSourceFactory; 
- /** 
- * 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现 
- * DBCP连接池就是java.sql.DataSource接口的一个具体实现 
- */ 
- public classJdbcUtils_DBCP { 
- private static DataSource ds = null; 
- //在静态代码块中创建数据库连接池 
- static{ 
- try{ 
- //加载dbcp.properties配置文件 
- InputStream in =JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties"); 
- Properties prop = new Properties(); 
- prop.load(in); 
- //创建数据源 
- ds =BasicDataSourceFactory.createDataSource(prop); 
- }catch (Exception e) { 
- throw newExceptionInInitializerError(e); 
- } 
- } 
- //从数据源中获取数据库连接 
- public static Connection getConnection()throws SQLException{ 
- //从数据源中获取数据库连接 
- return ds.getConnection(); 
- } 
- //释放连接 
- public static void release(Connection conn){ 
- if(conn!=null){ 
- try{ 
- //将Connection连接对象还给数据库连接池 
- conn.close(); 
- }catch (Exception e) { 
- e.printStackTrace(); 
- } 
- } 
- } 
- } 
步骤3:
在应用中获取连接
[java] view plaincopyprint?
- Connection conn = null; 
- PreparedStatement st = null; 
- ResultSet rs = null; 
- try{ 
- //获取数据库连接 
- conn =JdbcUtils_DBCP.getConnection(); 
- …… 
- }catch (Exception e) { 
- e.printStackTrace(); 
- }finally{ 
- //释放资源 
- JdbcUtils_DBCP.release(conn); 
- } 
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?
- <c3p0-config> 
- <!-- C3P0的缺省(默认)配置,--> 
- <!-- 如果在代码中“ComboPooledDataSourceds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源 --> 
- <default-config> 
- <property name="driverClass">com.mysql.jdbc.Driver</property> 
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/anysearch</property> 
- <property name="user">root</property> 
- <property name="password">123456</property> 
- <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default:3 --> 
- <property name="acquireIncrement">5</property> 
- <!--初始化的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3--> 
- <property name="initialPoolSize">10</property> 
- <!--连接池中保留的最小连接数--> 
- <property name="minPoolSize">5</property> 
- <!--连接池中保留的最大连接数。Default:15 --> 
- <property name="maxPoolSize">20</property> 
- <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --> 
- <property name="acquireRetryAttempts">30</property> 
- <!--两次连接中间隔时间,单位毫秒。Default: 1000 --> 
- <property name="acquireRetryDelay">1000</property> 
- <!--连接关闭时默认将所有未提交的操作回滚。Default: false --> 
- <property name="autoCommitOnClose">false</property> 
- </default-config> 
- <!-- C3P0的命名配置,--> 
- <!-- 如果在代码中“ComboPooledDataSourceds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源 --> 
- <named-config name="MySQL"> 
- <property name="driverClass">com.mysql.jdbc.Driver</property> 
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/test2</property> 
- <property name="user">root</property> 
- <property name="password">123456</property> 
- <property name="acquireIncrement">5</property> 
- <property name="initialPoolSize">10</property> 
- <property name="minPoolSize">5</property> 
- <property name="maxPoolSize">20</property> 
- </named-config> 
- </c3p0-config> 
还有更多可设置的参数,具体可查阅相关资料。
步骤2:
在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池
[java] view plaincopyprint?
- import java.sql.Connection; 
- import java.sql.ResultSet; 
- import java.sql.SQLException; 
- import java.sql.Statement; 
- import com.mchange.v2.c3p0.ComboPooledDataSource; 
- public class JdbcUtils_C3P0 { 
- private static ComboPooledDataSource ds =null; 
- //在静态代码块中创建数据库连接池 
- static{ 
- try{ 
- //通过代码创建C3P0数据库连接池 
- /*ds = new ComboPooledDataSource(); 
- ds.setDriverClass("com.mysql.jdbc.Driver"); 
- ds.setJdbcUrl("jdbc:mysql://localhost:3306/test"); 
- ds.setUser("root"); 
- ds.setPassword("123456"); 
- ds.setInitialPoolSize(10); 
- ds.setMinPoolSize(5); 
- ds.setMaxPoolSize(20);*/ 
- //通过读取C3P0的xml配置文件创建数据源,C3P0的xml配置文件c3p0-config.xml必须放在src目录下 
- //ds = newComboPooledDataSource();//使用C3P0的默认配置来创建数据源 
- ds = newComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源 
- }catch (Exception e) { 
- throw newExceptionInInitializerError(e); 
- } 
- } 
- //从数据源中获取数据库连接 
- public static Connection getConnection()throws SQLException{ 
- //从数据源中获取数据库连接 
- return ds.getConnection(); 
- } 
- //释放链接 
- public static void release(Connection conn){ 
- if(conn!=null){ 
- try{ 
- //将Connection连接对象还给数据库连接池 
- conn.close(); 
- }catch (Exception e) { 
- e.printStackTrace(); 
- } 
- } 
- } 
- } 
步骤3:
在应用中获取连接
[java] view plaincopyprint?
- Connection conn = null; 
- PreparedStatement st = null; 
- ResultSet rs = null; 
- try{ 
- //获取数据库连接 
- conn = JdbcUtils_C3P0.getConnection(); 
- …… 
- }catch (Exception e) { 
- e.printStackTrace(); 
- }finally{ 
- //释放资源 
- JdbcUtils_C3P0release(conn); 
- } 
其他连接池
此外,还有其他的连接池可供选择,比如使用比较广泛的Proxool。Proxool是一种Java数据库连接池技术。是sourceforge下的一个开源项目,这个项目提供一个健壮、易用的连接池,最为关键的是这个连接池提供监控的功能,方便易用,便于发现连接泄漏的情况。
proxool和 c3p0能够更好的支持高并发,但是在稳定性方面略逊于dpcp。
可根据项目的实际需要来选择连接池。
Java连接池详解的更多相关文章
- C3P0连接池详解及配置
		C3P0连接池详解及配置 本人使用的C3P0的jar包是:c3p0-0.9.1.jar <bean id = "dataSource" class = "com.m ... 
- Java线程池详解(二)
		一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ... 
- Java Hibernate 之连接池详解
		Hibernate支持第三方的连接池,官方推荐的连接池是C3P0,Proxool,以及DBCP.在配置连接池时需要注意的有三点: 一.Apche的DBCP在Hibernate2中受支持,但在Hiber ... 
- Android-Service基本用法、AIDL、Binder连接池详解
		本文介绍Service与Activity之间的通信,文章包含以下内容: 一.Service基本用法 二.通过AIDL实现Service与Activity跨进程通信 三.Binder连接池 四.使用Me ... 
- jedis连接池详解(Redis)
		转自:http://tianxingzhe.blog.51cto.com/3390077/1684306 原子性(atomicity): 一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都 ... 
- Tomcat 连接池详解
		(转) JDBC 连接池 org.apache.tomcat.jdbc.pool 是Apache-Commons DBCP连接池的一种替换或备选方案. 那究竟为何需要一个新的连接池? 原因如下: Co ... 
- 【java线程系列】java线程系列之java线程池详解
		一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在 ... 
- common-pool2连接池详解与使用
		我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ... 
- Java数据库连接池详解
		http://www.javaweb1024.com/java/JavaWebzhongji/2015/06/01/736.html 对于共享资源,有一个很著名的设计模式:资源池(Resource P ... 
随机推荐
- Python的设计哲学探究
			在Python shell中输入import this就会在屏幕上打印出来Python的设计哲学,如下: In [25]: import this The Zen of Python, by Tim ... 
- zabbix监控系列(1)之zabbix-server安装
			推荐使用yum来安装 第一步:LAMP平台 zabbix使用php开发的,所以依赖于LAMP或者LNMP平台,由于http+mysql用yum安装及其方便,所以我在这里使用yum安装. yum -y ... 
- SOA 和webservice 的区别
			http://blog.csdn.net/bingjing12345/article/details/7575566 Web service 的具体过程 需要明确的东西 1, 服务器端 和 客户端 之 ... 
- 开发板ping不通主机和虚拟机的看过来(转载)!
			前几天在做uboot下用tftp下载文件到开发板的实验时,为了能解决开发板ping不通主机和虚拟机的问题,可谓绞尽脑汁,正所谓久病成医,虽然为了这一小问题废了我那么长时间,但我在解决问题的同时也学到了 ... 
- 关于centos的yum代理设置
			10. 有代理服务器时使用 yum默认情况下,yum 通过 HTTP 连接到网络上的仓库.所有 yum HTTP 操作都使用 HTTP/1.1,与支持这一标准的 web 代理服务器兼容.您也可以连接到 ... 
- iOS / Android 移动设备中的 Touch Icons
			上次转载了一篇<将你的网站打造成一个iOS Web App>,但偶然发现这篇文章的内容有些是错误的——准确来说也不是错误,只是不适合自半年前来的情况了(也可以说是iOS7 之后的时间)—— ... 
- 获取<img src="sdf.jpg" Big="sf.jpg">中的big的值
			原代码: <img src="sdf.jpg" Big="sf.jpg" onclick="getsrc($(this).attr(" ... 
- Swift学习一
			// 定义一个类 class AClass { } // 数据转换 var a = Int(4.555) // 可选值 var num: Int? = Int("23k") // ... 
- C# 获取进程或线程的相关信息
			信息来自: http://blog.163.com/kunkun0921@126/blog/static/169204332201293023432113/ using System; using S ... 
- [Unity] Unity3D研究院编辑器之独立Inspector属性
			本文转自: http://www.xuanyusong.com/archives/3680雨松MOMO Unity提供了强大的Editor功能, 我们可以很轻易的在EditorGUI中绘制任意的属性. ... 
