目前有过接触的几个java的数据源有c3p0,dbcp,druid。再加上现在同样使用较多的proxool、BoneCP和tomcat的。准备把这些使用都再深入了解下的。
参考的是c3p0英文文档。
简介
要使用c3p0,需的引入相应的jar包:
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.11</version>
</dependency>
引入之后,配置相应的参数,即可直接使用:
import com.mchange.v2.c3p0.*;
...
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.postgresql.Driver" );
cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
cpds.setUser("dbuser");
cpds.setPassword("dbpassword");
当使用完毕,可以如下方式清理掉数据库:
cpds.close();
注意的是,c3p0需要JDK1.6+的环境。
c3p0提供了三种方式来创建池化数据源(支持连接池的数据源,简称池化数据源):
- 直接实例和配置ComboPooledDataSource对象。这是推荐最常用的一种方式。
- 使用 DataSources工厂类方式。
通过直接实例化PoolBackedDataSource,设置对应的ConectionPoolDataSource,创建自定义的池化数据源。
不管以何种方式创建的数据源,c3p0已经默认提供了相关配置参数。当然如果需要重设某些配置,可以通过以下方式:
通过java.util.Properties文件方式,c3p0.properties。
- 通过更先进的 HOCON配置文件方式,列如application.conf, application.json。
- 通过xml文件方式,c3p0-config.xml。
创建数据源
实例配置ComboPooledDataSource:
创建c3p0的池化数据源最简单的方式就是直接实例化 com.mchange.v2.c3p0.ComboPooledDataSource,然后配置相关参数即可。
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost/user_center");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("user");
dataSource.setPassword("password");
dataSource.setMaxStatements(10);
dataSource.setMaxPoolSize(5);
c2p0支持通过配置数据源名称达到一个数据库,多个数据源的方式:
ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
DataSources工厂方式:
通过通过静态工厂类com.mchange.v2.c3p0.DataSources先创建出非池化数据源,然后通过这个非池化数据源配置出池化数据源:
DataSource unpoolDs = DataSources.unpooledDataSource("jdbc:mysql://localhost/user_center", "root", "admin");
Map overrides = new HashMap();
overrides.put("maxStatements", "200");
overrides.put("maxPoolSize", new Integer(50));
DataSource poolDs = DataSources.pooledDataSource(unpoolDs, "testPoolDataSourc", overrides);
如上,如果要重设某些配置参数,可以通过Map配置;通过也可以给数据源配置名称。
池化数据源,不管是通过实例化ComboPooledDataSource还是通过 DataSources.pooledDataSource( … )获取的。其实都是继承实现了接口 com.mchange.v2.c3p0.PooledDataSource的。这个接口提供了很多查询数据源状态的方法。列如:
- public int getNumConnectionsDefaultUser()
- public int getNumConnections(String username, String password)
public int getNumConnectionsAllUsers()
c3p0通过不同的认证信息来区分维持不同的池化连接。因此要仔细辨别查询数据源状态方法是针对单个连接池的还是所有连接池的。列如maxPoolSize 是针对单个连接池的,getNumConnectionsAllUsers()针对所有的。如果你设置maxPoolSize为20,同时数据源配置两个不同的账号【一个是配置默认的,另外一个通过getConnection(user, password)】,则你通过getNumConnectionsAllUsers()查询出来的就是40。
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost/user_center");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("user");
dataSource.setPassword("password");
dataSource.setInitialPoolSize(4);
//注意,这里通过getConnection(user, password)。
Connection con = dataSource.getConnection("root", "admin");
System.out.println("NumConnections:"+dataSource.getNumConnections());
System.out.println("NumConnectionsAllUsers:"+dataSource.getNumConnectionsAllUsers());
System.out.println("NumConnectionsDefaultUser:"+dataSource.getNumConnectionsDefaultUser());
结果如下:
NumConnections:4
NumConnectionsAllUsers:8
NumConnectionsDefaultUser:4
当需要清理c3p0创建的DataSource时,很简单的操作就是使用静态 DataSources提供的销毁方法destroy(…)。 不过只有继承实现了PooledDataSource的数据源需要被清理。DataSources.destroy(…)对于非池化数据源不起作用。
DataSource ds_pooled = null;
try {
DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb", "swaldman", "test-password");
ds_pooled = DataSources.pooledDataSource( ds_unpooled );
....
} finally {
DataSources.destroy( ds_pooled );
}
同时,也有另外一种方式,c3p0的PooledDataSource接口提供了close()方法。
static void cleanup(DataSource ds) throws SQLException {
// make sure it's a c3p0 PooledDataSource
if ( ds instanceof PooledDataSource) {
PooledDataSource pds = (PooledDataSource) ds;
pds.close();
} else
System.err.println("Not a c3p0 PooledDataSource!");
}
ComboPooledDataSource继承实现了PooledDataSource,所以可以直接使用close()方法。
自定义创建数据源:
这个几乎很少用。可以自己参考文档Advanced: Building your own PoolBackedDataSource。这里不做笔记。
参数配置:
driverClass:
数据库驱动类名。默认为null。必须参数。
jdbcUrl:
数据库jdbc连接url地址。默认为null。必须参数。
user:
数据库连接账号。默认为null。必须参数。
password:
数据库连接密码。默认为null。必须参数。
dataSourceName:
数据源名称。如果有配置,则用配置的名称;如果没有,会使用连接池的标识token。【基本上可以忽略】
initialPoolSize:
启动初始化时,c3p0创建多少个连接。值要在minPoolSize和 maxPoolSize之间。默认3。
maxPoolSize:
连接池中最大连接数。默认15。
minPoolSize:
连接池中最新连接数。默认3。
maxStatements:
c3p0全局的PreparedStatement缓存大小。如果maxStatements 和maxStatementsPerConnection 都没有设置,PreparedStatement缓存将不会启用。【PreparedStatement作用】 maxStatements控制着所有连接的statement缓存大小,所以应该设置的足够大,才能满足每个连接中的缓存足够。【建议:首先计算出会被频繁使用的PreparedStatements有多少,然后乘以maxPoolSize得出合适的总缓存大小】虽然这个参数是c3p0的标准配置好参数,不过考虑到要经常遍历池中连接来判断是否达到最大缓存数,而导致性能上有影响,还是更建议用maxStatementsPerConnection 。默认0。
maxStatementsPerConnection:
连接池中单个连接缓存PreparedStatements 的数量大小。【建议:首先计算出会被频繁使用的PreparedStatements有多少,然后再加上2、3个】。默认0。
statementCacheNumDeferredCloseThreads:
延后清理statement缓存线程数。如果设置大于0,则Statement池会延后清理Statement缓存,直到所属的连接不被客户端或池内使用时。
对于一些JDBC驱动,尤其是Oracle,会在所属连接依然在使用时,关闭close掉Statement缓存。
当发现在连接关闭出现死锁现象时,可以设置这个参数。这个参数常常设置为1。如果要设置更大的话,建议同时也把maxStatements/maxStatementsPerConnection也设置更大,以达到无需经常搅动statement缓存。默认0。
当连接测试时发现损坏、达到空闲期限或太旧【connection age】时,连接池中的数量会减少。
acquireIncrement:
当池中连接耗尽,c3p0获取连接时,每次获取多少个连接。默认3。
acquireRetryAttempts:
当c3p0从数据库获取新连接失败时,重试次数。如果值小于或等于0,会无期限的重试。默认30。
acquireRetryDelay:
从数据库获取新连接重试间隔毫秒。默认1000。
breakAfterAcquireFailure:
设置为true时,当池化数据源重试acquireRetryAttempts次后依然获取新连接失败时,就会宣布自己损毁,同时关闭掉;
设置为false时,获取连接失败时,会抛出异常。但数据源依然还是保持有效,在下次请求getConnection()时,重新尝试从数据库中获取新连接。
默认false。
maxIdleTime:
空闲连接在被丢弃前存在多少秒。为0时表示空闲连接永不到期。默认0。
maxConnectionAge:
连接存活最大时间秒。超过的连接将会被摧毁并从池中清理掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。默认0。
maxIdleTimeExcessConnections:
超过minPoolSize的空闲连接在被剔除前存在最大时间秒。这个配置主要是为了减轻连接池的负载,比如连接池中连接数因为某次数据访问高峰导致创建了很多数据连接但是后面的时间段需要的数据库连接数很少,则此时连接池完全没有必要维护那么多的连接,所以有必要将断开丢弃掉一些连接来减轻负载,必须小于maxIdleTime,否则没有啥效果。配置不为0,则会将连接池中的连接数量保持到minPoolSize。默认0。
unreturnedConnectionTimeout:
连接未归还到连接池的时效长度,单位秒。如果大于0,则当连接在这个时效后依然没有归还时,会被毫不客气的销毁掉destroy()。这个能达到让有连接泄露的应用继续存活,而不是连接池耗尽。因此,这个时效长度应该设置足够的长,以保证正常合理的连接在运行调用时不被斩断。
不过这个依然是个不好的设置,毕竟从根本上修复连接泄露才是王道。默认0。
bugUnreturnedConnectionStackTraces:
是否记录返回连接池失败的记录日志。这个是意图用来debug调试连接泄露的。当设置为true时,连接池会捕捉每个连接取出时的堆栈足迹,当连接超过unreturnedConnectionTimeout时间未归还时,会记录异常日志。因为捕捉堆栈足迹会减慢连接的取出,所以建议在debug模式时设置,默认false。
checkoutTimeout:
当连接池耗尽,通过getConnection()等待取出连接时间,单位毫秒。设置为0时,表示一直等待直到获取连接。设置大于0时,当等到超过这个时间,就会抛出SQLException异常。默认0。
autoCommitOnClose:
JDBC的规则没有提供关于当连接关闭close()时,事务该如何处理。c3p0默认是回滚rollback未提交的事务。默认false。
forceIgnoreUnresolvedTransactions:
automaticTestTable:
如果配置了测试表名,则c3p0则会创建个这个名称的空表,同时使用自己的查询方式去测试连接情况。因此preferredTestQuery的任何设置都被忽视。不过这个表只能用于测试。
preferredTestQuery:
用于测试连接的测试sql语句。越简单越好。一般来说都是脱离表的,列如:select 1; select now(); 默认null。
idleConnectionTestPeriod:
每隔多少秒测试空闲连接。为0时表示不做测试。默认0。
testConnectionOnCheckin:
当连接归还时测试连接是否有效。当设置为true时,连接归还时,会异步检测该连接是否有效,跟idleConnectionTestPeriod组合使用很有效果。默认false。
testConnectionOnCheckout:
当连接获取时测试连接是否有效。当设置为true时,连接获取时,会测试该连接是否有效。因此考虑到性能,preferredTestQuery 或automaticTestTable要设置的有效果方便的。在取连接时,测试连接是否有效是最简单有效的。考虑到性能,还是建议定期检查idleConnectionTestPeriod。默认false。
c3p0还提供了其他相关配置参数,参数配置。
附录配置样例:
<!-- C3P0数据库配置 -->
<bean id="readerDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass"
value="${reader.jdbc.driverClassName}" />
<property name="jdbcUrl"
value="${reader.jdbc.url}" />
<property name="user"
value="${reader.jdbc.username}" />
<property name="password"
value="${reader.jdbc.password}" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="3" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30" />
<!-- 两次连接中间隔时间,单位毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000" />
<!-- 连接关闭时默认将所有未提交的操作回滚。Default: false -->
<property name="autoCommitOnClose" value="false" />
<!-- 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出 SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
<property name="checkoutTimeout" value="60000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="5" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="3" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="300" />
<!-- 最大空闲时间,600秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="600" />
<!-- c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么 属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试 使用。Default: null -->
<!--<property name="automaticTestTable" value="${oracle.jdbc.username}.C3P0_TESTTABLE" />-->
<property name="preferredTestQuery" value="select now()" />
<!-- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试 获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->
<property name="breakAfterAcquireFailure" value="false" />
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements" value="0" />
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection" value="0" />
<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads" value="3" />
<!-- 记录返回连接池失败的日志 -->
<property name="debugUnreturnedConnectionStackTraces" value="true" />
<!--240秒connection不返回连接池就被自动杀掉-->
<property name="unreturnedConnectionTimeout" value="60" />
<!--
这个配置主要是为了减轻连接池的负载,比如连接池中连接数因为某次数据访问高峰导致创建了很多数据连接
但是后面的时间段需要的数据库连接数很少,则此时连接池完全没有必要维护那么多的连接,所以有必要将
断开丢弃掉一些连接来减轻负载,必须小于maxIdleTime。配置不为0,则会将连接池中的连接数量保持到minPoolSize。
default : 0 单位 s
-->
<property name="maxIdleTimeExcessConnections" value="50" />
<!--
配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待
它close再断开。配置为0的时候则不会对连接的生存时间进行限制。
default : 0 单位 s
-->
<property name="maxConnectionAge" value="50" />
</bean>
<bean id="writerDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${writer.jdbc.driverClassName}" />
<property name="jdbcUrl" value="${writer.jdbc.url}" />
<property name="user" value="${writer.jdbc.username}" />
<property name="password" value="${writer.jdbc.password}" />
<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="3" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30" />
<!-- 两次连接中间隔时间,单位毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000" />
<!-- 连接关闭时默认将所有未提交的操作回滚。Default: false -->
<property name="autoCommitOnClose" value="false" />
<!-- 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出 SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
<property name="checkoutTimeout" value="60000" />
<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
<property name="initialPoolSize" value="5" />
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="3" />
<!-- 连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!-- 最大空闲时间,600秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="600" />
<!-- c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么 属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试 使用。Default: null -->
<property name="preferredTestQuery" value="select now()" />
<!-- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试 获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->
<property name="breakAfterAcquireFailure" value="false" />
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements" value="0" />
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection" value="0" />
<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads" value="3" />
<!-- 记录返回连接池失败的日志 -->
<property name="debugUnreturnedConnectionStackTraces" value="true" />
<!--240秒connection不返回连接池就被自动杀掉-->
<property name="unreturnedConnectionTimeout" value="240" />
</bean>
相关配置文件:
reader.jdbc.driverClassName=com.mysql.jdbc.Driver
reader.jdbc.url=jdbc:mysql://******:3307/db_**?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false
reader.jdbc.username=******
reader.jdbc.password=******
######
writer.jdbc.driverClassName=com.mysql.jdbc.Driver
writer.jdbc.url=jdbc:mysql://******:3307/db_**?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false
writer.jdbc.username=******
writer.jdbc.password=******