数据库连接池DBCP浅析

匿名 (未验证) 提交于 2019-12-03 00:05:01

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相关配置:
说明:
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<=numcols;i++) {                     System.out.print("\t" + rset.getString(i));                 }                 System.out.println("");             }         } catch(SQLException e) {             e.printStackTrace();         } finally {             try { if (rset != null) rset.close(); } catch(Exception e) { }             try { if (stmt != null) stmt.close(); } catch(Exception e) { }             try { if (conn != null) conn.close(); } catch(Exception e) { }         }     }      public static DataSource setupDataSource(String connectURI) {         BasicDataSource ds = new BasicDataSource();         ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");         ds.setUsername("scott");         ds.setPassword("tiger");         ds.setUrl(connectURI);         return ds;     }      public static void printDataSourceStats(DataSource ds) {         BasicDataSource bds = (BasicDataSource) ds;         System.out.println("NumActive: " + bds.getNumActive());         System.out.println("NumIdle: " + bds.getNumIdle());     }      public static void shutdownDataSource(DataSource ds) throws SQLException {         BasicDataSource bds = (BasicDataSource) ds;         bds.close();     } }  This ad is supporting your extension Smooth GesturesTurn off these ads?
用户在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 < initialSize ; i++) {                 connectionPool.addObject();             }         } catch (Exception e) {             throw new SQLNestedException("Error preloading the connection pool", e);         }                  return dataSource;     }
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等
一些性能比较的文档:

DBCP,C3P0,Tomcat_JDBC 性能及稳定性测试
各种数据库连接池对比

reference:
commons-dbcp

转载于:https://my.oschina.net/zipu888/blog/549599

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!