本文主要对连接池的基本原理以及dbcp的实现方式做一个分析,对dbcp的配置参数结合原理做一个简单解释。
连接池扼要
JDBC是一套通用的Java语言与多种数据库(文件)通讯的标准API。大部分针对数据库服务器(例如Oracle, MySQL等等)的JDBC实现都是基于TCP/IP连接的客户端-服务器端通讯方式。
当我们需要执行一个数据库操作时,有下面三步:
1. 客户端与服务器之间建立一个数据库连接
2. 执行某种数据库操作
3. 断开连接
如果每次处理都要走上面的三步,则应用程序与数据库服务器都要将大量的时间和资源消耗在数据连接的断开与建立上。对于并发较大的系统,建立一次连接然后缓存起来连续使用,直到程序结束等情况下再释放连接,就能够将系统资源集中在对数据库操作的处理上,从而大大提高性能。通常情况下将数据连接的建立和断开委托给一种能够数据库连接池的组件或服务进行管理。而DBCP, C3p0, Proxool等都是常用的开源的连接池组件。
就好像A公司在郊外,他们公司附近没有出租车。如果A公司有人要出去办事,他必须打电话给出租公司订车,用完车后他还要付账报销。 这样每个人出去一趟都必须订车、退车和报销。员工的很多时间白白花费在这上面了。于是A公司跟出租车公司定了一个合同,出租车公司给了他们一个车队。要用车随时去楼下找车队就可以了,用完了也不必结帐,A公司统一跟出租车公司订车和结帐。这个车队就好比连接池,由公司(应用程序)来统一向出租车公司(数据库服务器)订车(建立连接)和退车(关闭连接)。
DBCP的配置参数以及背后的原理
Commons-dbcp连接池的配置参数比较多,也比较复杂,主要分为
· Jdbc连接参数(username, password, url, driverClassName, connectionProperties )
· 事务处理参数 (defaultAutoCommit, defaultReadOnly, defaultTransactionIsolation, defaultCatalog)
· 连接池参数(详见下文)
· 连接池中链接存活性测试参数(详见下文)
· 预处理查询池化参数(poolPreparedStatements, maxOpenPreparedStatements)
· 丢弃失效链接相关参数(详见下文)以及一个控制是否可以正常情况下处于访问连接池包装下的底层JDBC链接参数(accessToUnderlyingConnectionAllowed)
其中Jdbc链接参数、事务处理都跟连接池关系不大,另预处理查询池化参数本文不详细叙述。有关commons-dbcp的详细参数配置信息请参考官方文档。
连接池的配置
在连接池中,这几个参数是十分重要的,官方的说明如下,是我们调节系统性能时需要认真考虑的值。
Parameter
| Default
| Description
|
initialSize | 0 | The initial number of connections that are created when the pool is started. |
maxActive | 8 | The maximum number of active connections that can be allocated from this pool at the same time, or non-positive for no limit. |
maxIdle | 8 | The maximum number of connections that can remain idle in the pool, without extra ones being released, or negative for no limit. |
minIdle | 0 | The minimum number of connections that can remain idle in the pool, without extra ones being created, or zero to create none. |
maxWait | indefinitely | The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or -1 to wait indefinitely. |
实际上,Dbcp 依赖于 commons-pool 来存储连接对象。 BasicDataSource默认使用GenericObjectPool来管理连接对象。除了请求的线程会在请求和返回连接过程中影响池中连接实例外,另有一个跑着GenericObjectPool.Evictor类型 (implements Runnable) 的实例的线程,也会影响池中的数据库连接。
池对象 : 就是要放到池容器中的对象 , 理论上可以是任何对象 .
对象池工厂 (ObjectPoolFactory 接口 ) : 用来创建对象池的工厂 , 这个没什么好说的 .
池对象工厂 (PoolableObjectFactory 接口 ) : 用来创建池对象 , 将不用的池对象进行钝化 (passivateObject), 对要使用的池对象进行激活 (activeObject), 对池对象进行验证 (validateObject), 对有问题的池对象进行销毁 (destroyObject) 等工作
例如在 tomcat 中,利用 JNDI 来查找到资源 javax.sql.DataSource 的实现,如果使用 dbcp 连接池,则该实现为 org.apache.commons.dbcp.BasicDataSource 。我们可以从这个称为 “ 数据源 ” 的类中调用 getConnection 方法来获得与数据库的连接。但内部是如何实现的呢?对象池在其中占据什么位置呢?这一切对于外部使用者来说是透明的。
BasicDataSource 的 getConnection 首先建立了 PoolingDataSource 对象来 getConnection 。这个 PoolingDataSource 对象中引用了 ObjectPool ,在 getConnection() 时,是从 ObjectPool 中借用了一个对象,既调用 ObjectPool.borrowObject() 方法。而对于熟悉 commons-pool 的程序员来说, ObjectPool 肯定有与之对应的 Factory 创建对象,并放到池中。因此 dbcp 通过实现了接口 org.apache.commons.pool.PoolableObjectFactory 的类 org.apache.commons.dbcp.PoolableConnectionFactory 的 makeObject 方法来创建连接 Connection 对象。然而 PoolableConnectionFactory 持有对 ConnectionFactory 的引用, ConnectionFactory 可以有 3 种策略来创建 Connection 对象。其中 DriverConnectionFactory 调用了数据库厂商提供的 Driver 来获得 Connection 。如图所示。
在创建连接时,是创建的 DelegatingConnection 实例,该对象持有对池的引用,对于 java.sql.Connectionl.close() 方法来说是调用的对象池的 returnObject 方法来返回池,而不是真正的把连接关掉。而我们在外界调用这个方法时,并未发现这一点。而对于调用 Connection 的其他方法而言,都是委派到由 Driver 得到的 Connection 相对应的方法。即 PoolableConnection-->Connection ,前者实现了 DelegatingConnection 。
在用 dbcp 连接池的时候,若设置了 poolPreparedStatements 属性为 true ,则 PoolableConnectionFactory 在 makeObject 时,建立的连接存在的委派关系如下: PoolableConnection-->PoolingConnection-->Connection 。 PoolingConnection 实现了 KeyedPoolableObjectFactory ,从而为与之对应的对象池 keyedObjectPool 形成双向关联。在对象池中,以 PStmtKey( 包含有 sql 等属性 ) 来标示该池中的一类对象。因此在使用 dbcp 连接池时,若配置了 poolPreparedStatements 属性,注意尽量保持相同功能的 sql 写法一致,来充分利用对象池。
转载于:https://blog.51cto.com/drizzlewalk/397982