一、C3P0 介绍
C3P0 是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有 hibernate,spring 等。c3p0 的出现,是为了大大提高应用程序和数据库之间访问效率的。
它的特性:
1、编码的简单易用
2、连接的复用
3、连接的管理
二、使用方法
1、下载最新 C3PO 包文件,下载地址:https://sourceforge.NET/projects/c3p0/files/
2、将上面的包文件中 lib 下的 c3p0-xxxx.jar 和 mchange-commons-Java- xxxx.jar 导入 eclipse 中,并在工程的 src 下新建一个名为 c3p0-config.xml,且内容为:
- <c3p0-config>
- <default-config>
- <property name="user">
- root
- </property>
- <property name="password">
- java
- </property>
- <property name="driverClass">
- com.mysql.jdbc.Driver
- </property>
- <property name="jdbcUrl">
- jdbc:mysql://localhost:3306/jdbc
- </property>
- <property name="initialPoolSize">
- 10
- </property>
- <property name="maxIdleTime">
- 30
- </property>
- <property name="maxPoolSize">
- 100
- </property>
- <property name="minPoolSize">
- 10
- </property>
- </default-config>
- <named-config name="mySource">
- <property name="user">
- root
- </property>
- <property name="password">
- java
- </property>
- <property name="driverClass">
- com.mysql.jdbc.Driver
- </property>
- <property name="jdbcUrl">
- jdbc:mysql://localhost:3306/jdbc
- </property>
- <property name="initialPoolSize">
- 10
- </property>
- <property name="maxIdleTime">
- 30
- </property>
- <property name="maxPoolSize">
- 100
- </property>
- <property name="minPoolSize">
- 10
- </property>
- </named-config>
- </c3p0-config>
参数配置说明:
1). 最常用配置
initialPoolSize: 连接池初始化时创建的连接数, default : 3(建议使用)
minPoolSize: 连接池保持的最小连接数, default : 3(建议使用)
maxPoolSize: 连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大, default : 15(建议使用)
acquireIncrement: 连接池在无空闲连接可用时一次性创建的新数据库连接数, default : 3(建议使用)
2). 管理连接池的大小和连接的生存时间
maxConnectionAge: 配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它 close 再断开。配置为 0 的时候则不会对连接的生存时间进行限制。default : 0 单位 s(不建议使用)
maxIdleTime: 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接。如果为 0,则永远不会断开连接, 即回收此连接。default : 0 单位 s(建议使用)
maxIdleTimeExcessConnections: 这个配置主要是为了快速减轻连接池的负载,比如连接池中连接数因为某次数据访问高峰导致创建了很多数据连接,但是后面的时间段需要的数据库连接数很少,需要快速释放,必须小于 maxIdleTime。其实这个没必要配置,maxIdleTime 已经配置了。default : 0 单位 s(不建议使用)
3). 配置连接测试:
automaticTestTable: 配置一个表名,连接池根据这个表名用自己的测试 sql 语句在这个空表上测试数据库连接, 这个表只能由 c3p0 来使用,用户不能操作。default : null(不建议使用)
preferredTestQuery: 与上面的 automaticTestTable 二者只能选一。自己实现一条 SQL 检测语句。default: null(建议使用)
idleConnectionTestPeriod: 用来配置测试空闲连接的间隔时间。测试方式还是上面的两种之一,可以用来解决 MySQL8 小时断开连接的问题。因为它保证连接池会每隔一定时间对空闲连接进行一次测试,从而保证有效的空闲连接能每隔一定时间访问一次数据库,将于 MySQL8 小时无会话的状态打破。为 0 则不测试。default: 0(建议使用)
testConnectionOnCheckin: 如果为 true,则在 close 的时候测试连接的有效性。default : false(不建议使用)
testConnectionOnCheckout: 性能消耗大。如果为 true,在每次 getConnection 的时候都会测试,为了提高性能, 尽量不要用。default : false(不建议使用)
4). 配置 PreparedStatement 缓存:
maxStatements: 连接池为数据源缓存的 PreparedStatement 的总数。由于 PreparedStatement 属于单个 Connection, 所以这个数量应该根据应用中平均连接数乘以每个连接的平均 PreparedStatement 来计算。同时 maxStatementsPerConnection 的配置无效。default : 0(不建议使用)
maxStatementsPerConnection: 连接池为数据源单个 Connection 缓存的 PreparedStatement 数,这个配置比 maxStatements 更有意义,因为它缓存的服务对象是单个数据连接,如果设置的好,肯定是可以提高性能的。为 0 的时候不缓存。default : 0(看情况而论)
5). 重连相关配置
acquireRetryAttempts: 连接池在获得新连接失败时重试的次数,如果小于等于 0 则无限重试直至连接获得成功。default : 30(建议使用)
acquireRetryDelay: 连接池在获得新连接时的间隔时间。default : 1000 单位 ms(建议使用)
breakAfterAcquireFailure: 如果为 true,则当连接获取失败时自动关闭数据源,除非重新启动应用程序。所以一般不用。default : false(不建议使用)
checkoutTimeout: 配置当连接池所有连接用完时应用程序 getConnection 的等待时间。为 0 则无限等待直至有其他连接释放或者创建新的连接,不为 0 则当时间到的时候如果仍没有获得连接,则会抛出 SQLException。其实就是 acquireRetryAttempts*acquireRetryDelay。default : 0(与上面两个,有重复,选择其中两个都行)
6). 定制管理 Connection 的生命周期
connectionCustomizerClassName: 用来定制 Connection 的管理,比如在 Connection acquire 的时候设定 Connection 的隔离级别,或者在 Connection 丢弃的时候进行资源关闭,
就可以通过继承一个 AbstractConnectionCustomizer 来实现相关方法,配置的时候使用全类名。有点类似监听器的作用。default : null(不建议使用)
7). 配置未提交的事务处理
autoCommitOnClose: 连接池在回收数据库连接时是否自动提交事务。如果为 false,则会回滚未提交的事务,如果为 true,则会自动提交事务。default : false(不建议使用)
forceIgnoreUnresolvedTransactions:这个配置强烈不建议为 true。default: false(不建议使用)
一般来说事务当然由自己关闭了,为什么要让连接池来处理这种不细心问题呢?
8). 配置 debug 和回收 Connection
unreturnedConnectionTimeout: 为 0 的时候要求所有的 Connection 在应用程序中必须关闭。如果不为 0,则强制在设定的时间到达后回收 Connection,所以必须小心设置,保证在回收之前所有数据库操作都能够完成。这种限制减少 Connection 未关闭情况的不是很适用。建议手动关闭。default : 0 单位 s(不建议使用)
debugUnreturnedConnectionStackTraces: 如果为 true 并且 unreturnedConnectionTimeout 设为大于 0 的值,当所有被 getConnection 出去的连接 unreturnedConnectionTimeout 时间到的时候,就会打印出堆栈信息。只能在 debug 模式下适用,因为打印堆栈信息会减慢 getConnection 的速度 default : false(不建议使用)
3)ConnectionPool 工具类
- package com.study.cc;
- import java.sql.Connection;
- import java.sql.SQLException;
- import javax.sql.DataSource;
- import com.mchange.v2.c3p0.ComboPooledDataSource;
- public class ConnectionPool {
- private DataSource ds;
- private static ConnectionPool pool;
- private ConnectionPool(){
- ds = new ComboPooledDataSource("mySource");
- }
- public static final ConnectionPool getInstance(){
- if(pool==null){
- try{
- pool = new ConnectionPool();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- return pool;
- }
- public synchronized final Connection getConnection() {
- try {
- return ds.getConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
// 其中的 mySource 是 c3p0-config.xml 中 named-config 的 name 值。DataSource 的实例使用 ComboPooledDataSource 来生成的。
4、调用
- package com.study.cc;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- public class TestMain {
- public static void main(String[] args) {
- ConnectionPool pool = ConnectionPool.getInstance();
- Connection con = null;
- PreparedStatement stmt= null;
- ResultSet rs = null;
- try{
- con = pool.getConnection();
- stmt = con.prepareStatement("select sysdate as nowtime from dual");
- rs = stmt.executeQuery();
- while(rs.next()){
- System.out.println("nowtime="+rs.getString("nowtime"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- try {
- rs.close();
- stmt.close();
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
三、注意事项:
1、C3P0 错误 APPARENT DEADLOCK!!! 解决
在使用 C3P0 的过程中出现了好多错误. 最长见就是死锁, 占用资源比较大. 对于死锁问题 http://japi.iteye.com/blog/243702. 找到了解决方案 (不能完全保证, 但在负载测试中, 还没有出现问题) 。
问题关键是参数的设置,说明如下:
属于单个 connection 而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果 maxStatements 与 maxStatementsPerConnection 均为 0,则缓存被关闭。Default: 0-->
解决方法:
Inhibernate.cfg.xml:
In c3p0.properties: c3p0.maxStatements=0 c3p0.maxStatementsPerConnection=100
2、 使用 c3p0 连接池,造成的 MySql 8 小时问题
对 c3p0 与 DBCP 连接池连接 MySQL 数据库时, 8 小时内无请求自动被 mysql 断开连接,c3p0 再请求时之后访问时会出现第一次访问报错,再次访问正常的现象。
报错日志:
- org.springframework.transaction.CannotCreateTransactionException:Could not open JPA EntityManager for transaction; nested exception isjavax.persistence.PersistenceException: org.hibernate.TransactionException:JDBC begin transaction failed:
- atorg.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:428)
- atorg.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
- atorg.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)
- atorg.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)
- atorg.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
- atorg.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
- atorg.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
- atcom.appcarcare.cube.service.UserService$$EnhancerByCGLIB$$a4429cba.getUserDao(<generated>)
- atcom.appcarcare.cube.servlet.DataCenterServlet$SqlTimer.connectSql(DataCenterServlet.java:76)
- atcom.appcarcare.cube.servlet.DataCenterServlet$SqlTimer.run(DataCenterServlet.java:70)
- atjava.util.TimerThread.mainLoop(Timer.java:555)
- atjava.util.TimerThread.run(Timer.java:505)
- Caused by:javax.persistence.PersistenceException: org.hibernate.TransactionException:JDBC begin transaction failed:
- atorg.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
- atorg.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
- atorg.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1397)
- atorg.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:62)
- atorg.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:71)
- atorg.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:60)
- atorg.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:378)
- ...11 more
- Caused by: org.hibernate.TransactionException:JDBC begin transaction failed:
- atorg.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:76)
- atorg.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:160)
- atorg.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1426)
- atorg.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:59)
- ...14 more
- Caused by:com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications linkfailure
- The last packet successfully received from theserver was 1,836,166 milliseconds ago. The last packet sent successfully to the server was 29,134 millisecondsago.
- atsun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
- atsun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
- atsun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
- atjava.lang.reflect.Constructor.newInstance(Constructor.java:526)
- atcom.mysql.jdbc.Util.handleNewInstance(Util.java:411)
- atcom.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1117)
- atcom.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3567)
- atcom.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)
- atcom.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3997)
- atcom.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
- atcom.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)
- atcom.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2713)
- atcom.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:5060)
- atcom.mchange.v2.c3p0.impl.NewProxyConnection.setAutoCommit(NewProxyConnection.java:881)
- atorg.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:72)
- ...17 more
- Caused by: java.net.SocketException: Softwarecaused connection abort: recv failed
- atjava.net.SocketInputStream.socketRead0(Native Method)
- atjava.net.SocketInputStream.read(SocketInputStream.java:150)
- atjava.net.SocketInputStream.read(SocketInputStream.java:121)
- atcom.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
- atcom.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
- atcom.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
- atcom.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3014)
- atcom.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3467)
- ...25 more
MySQL 服务器默认的 "wait_timeout" 是 28800 秒即 8 小时,意味着如果一个连接的空闲时间超过 8 个小时,MySQL 将自动断开该连接,而连接池却认为该连接还是有效的 (因为并未校验连接的有效性),当应用申请使用该连接时,就会导致上面的报错。
解决方法(有两种方法):
1)减少连接池内连接的生存周期
减少连接池内连接的生存周期,使之小于上一项中所设置的 wait_timeout 的值。
修改 c3p0 的配置文件,在 Spring 的配置文件中设置:
- <beanid="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <propertyname="maxIdleTime"value="1800"/>
- <!--otherproperties -->
- </bean>
2)定期使用连接池内的连接
定期使用连接池内的连接,使得它们不会因为闲置超时而被 MySQL 断开。
修改 c3p0 的配置文件, 在 Spring 的配置文件中设置:
- <bean id="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <propertyname="preferredTestQuery" value="SELECT 1"/>
- <propertyname="idleConnectionTestPeriod" value="18000"/>
- <propertyname="testConnectionOnCheckout" value="true"/>
- </bean>
四、数据库连接池性能比对 (hikaridruid c3p0 dbcp jdbc)
测试结论
1:性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0。hikariCP 的高性能得益于最大限度的避免锁竞争。
2:druid 功能最为全面,sql 拦截等功能,统计数据较为全面,具有良好的扩展性。
3:综合性能,扩展性等方面,可考虑使用 druid 或者 hikariCP 连接池。
4:可开启 prepareStatement 缓存,对性能会有大概 20% 的提升。
功能对比
功能 |
dbcp |
druid |
c3p0 |
tomcat-jdbc |
HikariCP |
是否支持 PSCache |
是 |
是 |
是 |
否 |
否 |
监控 |
jmx |
jmx/log/http |
jmx,log |
jmx |
jmx |
扩展性 |
弱 |
好 |
弱 |
弱 |
弱 |
sql 拦截及解析 |
无 |
支持 |
无 |
无 |
无 |
代码 |
简单 |
中等 |
复杂 |
简单 |
简单 |
特点 |
依赖于 common-pool |
阿里开源,功能全面 |
历史久远,代码逻辑复杂,且不易维护 |
优化力度大,功能简单,起源于 boneCP |
|
连接池管理 |
LinkedBlockingDeque |
数组 |
FairBlockingQueue |
threadlocal+CopyOnWriteArrayList |
proxool 网上有评测说在并发较高的情况下会出错,proxool 便没有进行调研。 由于 boneCP 被 hikariCP 替代,并且已经不再更新,boneCP 没有进行调研。
druid 的功能比较全面,且扩展性较好,比较方便对 jdbc 接口进行监控跟踪等。
c3p0 历史悠久,代码及其复杂,不利于维护。并且存在 deadlock 的潜在风险。
具体参考 http://blog.csdn.net/qq_31125793/article/details/51241943
来源: http://blog.csdn.net/byxdaz/article/details/69823421