您好,欢迎访问一九零五行业门户网

数据库连接池DBCP浅析

一个典型的关系数据库应用: a. 建立连接 b. 执行数据库操作 c. 关闭连接 其中建立连接可能需要数ms才能完成。对于多并发请求的场景,频繁的建立连接、断开连接可能会成为程序与关系数据库交互的瓶颈。 为此,很多时候,我们使用数据库连接池来复用连接,避免
一个典型的关系数据库应用:
a. 建立连接
b. 执行数据库操作
c. 关闭连接其中建立连接可能需要数ms才能完成。对于多并发请求的场景,频繁的建立连接、断开连接可能会成为程序与关系数据库交互的瓶颈。
为此,很多时候,我们使用数据库连接池来复用连接,避免频繁的建立连接带来的资源浪费。
dbcp是应用非常广泛的关系数据库连接池,和dbcp类似的有c3p0, proxool
dbcp依赖commons-pool提供的连接池,其包括两个不同的版本:
a. dbcp 1.4只能运行在jdk1.6(jdbc 4)
b. dbcp 1.3只能运行在jdk 1.4-1.5(jdbc 3)
dbcp相关配置:dbcp configuration
说明:
datasource: 要连接的 datasource (通常我们不会定义在 server.xml)
defaultautocommit: 对于事务是否 autocommit, 默认值为 true
defaultreadonly: 对于数据库是否只能读取, 默认值为 false
driverclassname:连接数据库所用的 jdbc driver class,
maxactive: 可以从对象池中取出的对象最大个数,为0则表示没有限制,默认为8
maxidle: 最大等待连接中的数量,设 0 为没有限制 (对象池中对象最大个数)
minidle:对象池中对象最小个数
maxwait: 最大等待秒数, 单位为 ms, 超过时间会丟出错误信息
password: 登陆数据库所用的密码
url: 连接数据库的 url
username: 登陆数据库所用的帐号
validationquery: 验证连接是否成功, sql select 指令至少要返回一行
removeabandoned: 是否自我中断, 默认是 false
removeabandonedtimeout: 几秒后会自我中断, removeabandoned 必须为 true
logabandoned: 是否记录中断事件, 默认为 false
minevictableidletimemillis:大于0 ,进行连接空闲时间判断,或为0,对空闲的连接不进行验证;默认30分钟
timebetweenevictionrunsmillis:失效检查线程运行时间间隔,如果小于等于0,不会启动检查线程,默认-1
testonborrow:取得对象时是否进行验证,检查对象是否有效,默认为false
testonreturn:返回对象时是否进行验证,检查对象是否有效,默认为false
testwhileidle:空闲时是否进行验证,检查对象是否有效,默认为false
initialsize:初始化线程数
dbcp原理
用户使用数据库连接池流程:
a. 从连接池中获取一个连接(如果有已建立空闲的连接,直接获取连接,否则建立新连接)
b. 执行数据库操作
c. 将连接归还给数据库连接池
源码分析(common-dbcp 1.4)
basicdatasource是用户使用的类,dbcp项目组放出example:import javax.sql.datasource;import java.sql.connection;import java.sql.statement;import java.sql.resultset;import java.sql.sqlexception;import org.apache.commons.dbcp2.basicdatasource;public class basicdatasourceexample { public static void main(string[] args) { // first we set up the basicdatasource. // normally this would be handled auto-magically by // an external configuration, but in this example we'll // do it manually. // system.out.println(setting up data source.); datasource datasource = setupdatasource(args[0]); system.out.println(done.); // // now, we can use jdbc datasource as we normally would. // connection conn = null; statement stmt = null; resultset rset = null; try { system.out.println(creating connection.); conn = datasource.getconnection(); system.out.println(creating statement.); stmt = conn.createstatement(); system.out.println(executing statement.); rset = stmt.executequery(args[1]); system.out.println(results:); int numcols = rset.getmetadata().getcolumncount(); while(rset.next()) { for(int i=1;i用户在new 一个basicdatasource时,并未真正建立连接池,建立连接是在用户首次使用getconnection获取连接时发生。
getconnection调用createdatasouce获取datasource,其用synchronized修饰,保证同一时刻最多只有一个线程执行该段代码
如下:
public connection getconnection() throws sqlexception { return createdatasource().getconnection(); }
protected synchronized datasource createdatasource() throws sqlexception { if (closed) { throw new sqlexception(data source is closed); } // return the pool if we have already created it if (datasource != null) { return (datasource); } // 用户第一次使用getconnection请求连接时,建立数据库连接池 connectionfactory driverconnectionfactory = createconnectionfactory(); //建立连接池,使用commons-pool的genericobjectpool createconnectionpool(); // set up statement pool, if desired generickeyedobjectpoolfactory statementpoolfactory = null; if (ispoolpreparedstatements()) { statementpoolfactory = new generickeyedobjectpoolfactory(null, -1, // unlimited maxactive (per key) generickeyedobjectpool.when_exhausted_fail, 0, // maxwait 1, // maxidle (per key) maxopenpreparedstatements); } // 设置连接池工厂 createpoolableconnectionfactory(driverconnectionfactory, statementpoolfactory, abandonedconfig); // 建立数据库连接池实例 createdatasourceinstance(); // 根据配置,初始化建立一些数据库连接 try { for (int i = 0 ; i protected void createdatasourceinstance() throws sqlexception { poolingdatasource pds = new poolingdatasource(connectionpool); pds.setaccesstounderlyingconnectionallowed(isaccesstounderlyingconnectionallowed()); pds.setlogwriter(logwriter); datasource = pds; }
poolingdatasource封装数据库连接池,其实现了getconnection,如下: public connection getconnection() throws sqlexception { try { connection conn = (connection)(_pool.borrowobject()); if (conn != null) { conn = new poolguardconnectionwrapper(conn); } return conn; } catch(sqlexception e) { throw e; } catch(nosuchelementexception e) { throw new sqlnestedexception(cannot get a connection, pool error + e.getmessage(), e); } catch(runtimeexception e) { throw e; } catch(exception e) { throw new sqlnestedexception(cannot get a connection, general error, e); } }
poolableobjectfactory是生成连接池对象的工厂,其实现了makeobject,如下:
public object makeobject() throws exception { connection conn = _connfactory.createconnection(); if (conn == null) { throw new illegalstateexception(connection factory returned null from createconnection); } initializeconnection(conn); if(null != _stmtpoolfactory) { keyedobjectpool stmtpool = _stmtpoolfactory.createpool(); conn = new poolingconnection(conn,stmtpool); stmtpool.setfactory((poolingconnection)conn); } return new poolableconnection(conn,_pool,_config); }
poolableconnection是连接代理,其实现了close,close并不真正关闭连接,而是调用_pool.returnobject(this)将连接还给对象池:
public synchronized void close() throws sqlexception { if (_closed) { // already closed return; } ...... if (!isunderlyingconectionclosed) { // normal close: underlying connection is still open, so we // simply need to return this proxy to the pool try { _pool.returnobject(this); // xxx should be guarded to happen at most once } catch(illegalstateexception e) { ...... }
上述borrowobject和returnobject都是genericobjectpool提供的接口,分别用来从对象池中获取对象和归还对象。
getconnection序列图如下:
其它开源数据库连接池:c3p0、bonecp、proxool、阿里巴巴的druid等
一些性能比较的文档:
c3p0、dbcp、proxool、bonecp比较 
dbcp,c3p0,tomcat_jdbc 性能及稳定性测试
各种数据库连接池对比
reference:
commons-dbcp
其它类似信息

推荐信息