一 介绍
上一篇文章中讲到了通过Connection去访问并操作数据库,每次连接数据库都是创建一个Connection对象,访问完毕再close掉连接,如下:
这种方式的缺点很明显:用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、严重的话甚至宕机。
那有没有什么可优化的办法呢?答案是肯定的,那就是 数据库连接池技术(数据源)。
二 使用数据库连接池优化程序性能
原理:应用程序在启动的时候就初始化一批Connection对象到一个池子中(集合),当应用程序需要访问数据库的时候就从这个池子中取Connection对象,操作数据库完成后再将这个Connection对象对象放回到池子中去。这样就可以避免重复创建Connection对象,如下:
三 实现自己的数据库连接池
实现自己的连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
package com.ricky;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;
import javax.sql.DataSource;
import com.itheima.exception.NotSupportException;
public class MyDataSource implements DataSource{
private static LinkedList pool = new LinkedList();
static{
try{
InputStream in = SimpleConnectionPool.class.getClassLoader().getResourceAsStream("dbcfg.properties");
Properties props = new Properties();
props.load(in);
Class.forName(props.getProperty("className"));
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(props.getProperty("url"), props.getProperty("username"), props.getProperty("password"));
pool.add(conn);
}
for(Connection conn:pool){
System.out.println("初始化的连接为:"+conn);
}
}catch(Exception e){
throw new ExceptionInInitializerError(e);
}
}
public LinkedList getPool(){
return pool;
}
@Override
public synchronized Connection getConnection() throws SQLException {
//conn.commit() conn.pro conn.close()
if(pool.size()>0){
final Connection conn = pool.removeFirst();
//得到代理对象的实例
Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(!method.getName().equals("close")){
//调用原有对象的方法
return method.invoke(conn, args);
}else{
//close
return pool.add(conn);
}
}
});
System.out.println(proxyConn.getClass().getName());
return proxyConn;
}else{
throw new RuntimeException("The Server is busy");
}
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public PrintWriter getLogWriter() throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public int getLoginTimeout() throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
@Override
public T unwrap(Class iface) throws SQLException {
throw new NotSupportException("This method not supported by this datasource");
}
}
四 开源数据库连接池
现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
1、DBCP
DBCP 是 Apache 软件基金组织下的开源连接池实现,Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
官网地址:http://commons.apache.org/proper/commons-dbcp/
使用方法
1.1、dbcp.properties
#连接数据库所用的 JDBC Driver Class,
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day14
username=root
password=sorry
#
initialSize=10
#连接池中可同时连接的最大的连接数,为0则表示没有限制,默认为8
maxActive=20
#
maxIdle=15
#
minIdle=5
#
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=UTF8
#对于事务是否 autoCommit, 默认值为 true
defaultAutoCommit=true
#对于数据库是否只能读取, 默认值为 false
defaultReadOnly=
#默认事物隔离几本,取值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ
基础参数说明
defaultAutoCommit: 对于事务是否 autoCommit, 默认值为 true
defaultReadOnly:对于数据库是否只能读取, 默认值为 false
initialSize:连接池启动时创建的初始化连接数量(默认值为0)
driverClassName:连接数据库所用的 JDBC Driver Class,
url: 连接数据库的 URL
username:登陆数据库所用的帐号
password: 登陆数据库所用的密码
maxActive: 连接池中可同时连接的最大的连接数,为0则表示没有限制,默认为8
maxIdle: 连接池中最大的空闲的连接数(默认为8,设 0 为没有限制),超过的空闲连接将被释放,如果设置为负数表示不限制(maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建)
minIdle:连接池中最小的空闲的连接数(默认为0,一般可调整5),低于这个数量会被创建新的连接(该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接)
maxWait: 超过时间会丟出错误信息 最大等待时间(单位为 ms),当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为-1,一般可调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
validationQuery: 验证连接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned:超过removeAbandonedTimeout时间后,是否进行没用连接的回收(默认为false)
removeAbandonedTimeout: 超过时间限制,回收五用的连接(默认为 300秒),removeAbandoned 必须为 true
logAbandoned: 是否记录中断事件, 默认为 false
1.2 示例代码
package com.itheima.pool;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class DBCPDemo1 {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
InputStream in = DBCPDemo1.class.getClassLoader().getResourceAsStream("dbcp.properties");
props.load(in);
BasicDataSourceFactory facotry = new BasicDataSourceFactory();
DataSource ds = facotry.createDataSource(props);
Connection conn = ds.getConnection();
System.out.println(conn.getClass().getName());
conn.close();//换回池中
}
}
2、C3P0
C3P0是一个更加强大、配置更灵活的数据源,官网地址:http://www.mchange.com/projects/c3p0/
2.1 通过代码设置各个配置项
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(props.getProperty("driverClass"));
cpds.setJdbcUrl(props.getProperty("jdbcUrl"));
cpds.setUser(props.getProperty("user"));
cpds.setPassword(props.getProperty("password"));
类路径下提供一个c3p0-config.xml文件,如下:
com.mysql.jdbc.Driver
jdbc:mysql:///day14
root
sorry
15
30
20
5
2000
com.mysql.jdbc.Driver
jdbc:mysql:///day14
root
sorry
15
30
20
5
2000
//ComboPooledDataSource ds = new ComboPooledDataSource();//获取配置文件中的default-config
ComboPooledDataSource ds = new ComboPooledDataSource("day14");
Connection conn = ds.getConnection();
更详细的用法可参考这篇文章:http://my.oschina.net/lyzg/blog/55133
JDBC数据源的讲解到这里就结束了,后续文章会介绍一下数据库操作框架,例如DBUtils、MyBatis等等。