对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所形成的问题。为解决咱们的问题,能够采用数据库链接池技术。数据库链接池的基本思想就是为数据库链接创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。咱们能够经过设定链接池最大链接数来防止系统无尽的与数据库链接。更为重要的是咱们能够经过链接池的管理机制监视数据库的链接的数量﹑使用状况,为系统开发﹑测试及性能调整提供依据。html
链接,是咱们的编程语言与数据库交互的一种方式。咱们常常会听到这么一句话“数据库链接很昂贵“。java
有人接受这种说法,殊不知道它的真正含义。所以,下面经过实例解释它到底是什么。mysql
下面是Mysql数据库建立链接的的一段代码:web
[java] view plaincopyprint?sql
String connUrl ="jdbc:mysql://your.database.domain/yourDBname"; 数据库
Class.forName("com.mysql.jdbc.Driver"); apache
Connection con =DriverManager.getConnection (connUrl); 编程
当咱们建立了一个Connection对象,它在内部都执行了什么:c#
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万次链接,极大的浪费数据库的资源,而且极易形成数据库服务器内存溢出、拓机。
采用链接池技术后的过程以下:
数据库链接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤其突出。对数据库链接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库链接池负责分配,管理和释放数据库链接,它容许应用程序重复使用一个现有的数据库链接,而不是从新创建一个。
一、并发问题
为了使链接管理服务具备最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,由于各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)、lock(C#)关键字便可确保线程是同步的。
二、事务处理
咱们知道,事务具备原子性,此时要求对数据库的操做符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全作,要么全不作。
咱们知道当2个线程公用一个链接Connection对象,并且各自都有本身的事务要处理时候,对于链接池是一个很头疼的问题,由于即便Connection类提供了相应的事务支持,但是咱们仍然不能肯定那个数据库操做是对应那个事务的,这是因为咱们有2个线程都在进行事务操做而引发的。为此咱们可使用每个事务独占一个链接来实现,虽然这种方法有点浪费链接池资源可是能够大大下降事务管理的复杂性。
3、链接池的分配与释放
链接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,能够提升链接的复用度,从而下降创建新链接的开销,同时还能够加快用户的访问速度。
对于链接的管理可以使用一个List。即把已经建立的链接都放入List中去统一管理。每当用户请求一个链接时,系统检查这个List中有没有能够分配的链接。若是有就把那个最合适的链接分配给他(如何能找到最合适的链接文章将在关键议题中指出);若是没有就抛出一个异常给用户,List中链接是否能够被分配由一个线程来专门管理。
4、链接池的配置与维护
链接池中到底应该放置多少链接,才能使系统的性能最佳?系统可采起设置最小链接数(minConnection)和最大链接数(maxConnection)等参数来控制链接池中的链接。比方说,最小链接数是系统启动时链接池所建立的链接数。若是建立过多,则系统启动就慢,但建立后系统的响应速度会很快;若是建立过少,则系统启动的很快,响应起来却慢。这样,能够在开发时,设置较小的最小链接数,开发起来会快,而在系统实际使用时设置较大的,由于这样对访问客户来讲速度会快些。最大链接数是链接池中容许链接的最大数目,具体设置多少,要看系统的访问量,可经过软件需求上获得。
如何确保链接池中的最小链接数呢?有动态和静态两种策略。动态即每隔必定时间就对链接池进行检测,若是发现链接数量小于最小链接数,则补充相应数量的新链接,以保证链接池的正常运转。静态是发现空闲链接不够时再去检查。
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 是 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是一个开源的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。
可根据项目的实际须要来选择链接池。