一、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.找到了解决方案(不能完全保证,但在负载测试中,还没有出现问题) 。
问题关键是参数的设置,说明如下:
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<propertyname="maxStatementsPerConnection"></property>
解决方法:
Inhibernate.cfg.xml:
<property name="hibernate.c3p0.max_statements">0</property>
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