当前位置: 首页 > 科技观察

项目中使用了C3P0作为数据库连接池,被技术负责人惊呆了

时间:2023-03-23 11:31:33 科技观察

本文转载自微信公众号《Java极客技术》,鸭血粉作者。转载本文请联系Java极客技术公众号。1.简介数据库连接是一种非常关键、有限且昂贵的资源,尤其是在多用户Web应用程序中。我记得我以前做过的一个项目。当时应用配置的c3p0数据库连接池允许最大连接数为500,结果上线没多久,并发直接增加,导致大量数据插入失败。我可以想象那天晚上的心情。知乎~那次事故后,我对应用中的数据库连接数有了深刻的认识。为了防止再被绊倒,我专门花时间写了一个程序测试用例来测试各个数据源的连接池的稳定性。以免自己再次入坑!话不多说,开始吧!2.程序示例熟悉web系统开发的同学基本都知道,Java生态中常见的开源数据库连接池有以下几种类型:dbcp:DBCP是一个依赖于Jakartacommons-pool对象池的数据库连接池机制。DBCP可以直接在应用程序中使用。Tomcat的数据源使用DBCPc3p0:c3p0是一个开源的JDBC连接池。它在lib目录中与Hibernate一起发布,包括实现jdbc3和jdbc2扩展规范中描述的Connection和Statement池的DataSources对象。一个ProxyDriver,一系列内置的JDBC组件库,一个SQLParser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQLServer、H2等。今天我们就将这三种数据源连接池的稳定性放在一起进行比较。2.1.创建测试表下面以mysql数据库为例,先创建一个t_test表,后面插入数据操作。CREATETABLEt_test(idbigint(20)unsignedNOTNULLCOMMENT'主键ID',namevarchar(32)NOTNULLCOMMENT'name',PRIMARYKEY(id))ENGINE=InnoDBCOMMENT='测试表';2.2.编写测试用例以dbcp为例,首先创建一个dbcp-jdbc.properties配置文件。username=rootpassword=Hello@123456driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://192.168.31.200:3306/testdb?useUnicode=true&characterEncoding=UTF-8initialSize=5maxActive=1000maxIdle=5removeAbandoned=tureremoveAbandonedTimeout=20logWAbandmax下一个,创建连接池工具DbcpJdbcUtil。publicclassDbcpJdbcUtil{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(DbcpJdbcUtil.class);/**jdbc配置文件*/privatestaticPropertiesprop=newProperties();privatestaticBasicDataSourcedataSource=null;//是只事务连接!privatestaticThreadLocaltl=newThreadLocal();static{classPathSourceRead();}privatestaticvoidclassPathSourceRead(){//读取指定位置的配置文件(读取class目录文件)try{logger.info("jdbc路径:"+SysConstants.getValue());prop.load(DbcpJdbcUtil.class.getClassLoader().getResourceAsStream(SysConstants.getValue()));logger.info("数据配置信息"+JSON.toJSONString(prop));logger.info("初始化默认jdbc配置文件成功!");}catch(Exceptione){logger.error("初始化默认jdbc文件失败!",e);}}/***从中获取数据源连接池*@return*@throwsException*/publicstaticBasicDataSourcegetDataSource()throwsException{try{if(dataSource==null){synchronized(DbcpJdbcUtil.class){if(dataSource==null){dataSource=newBasicDataSource();dataSource.setUsername(prop.getProperty("用户name"));dataSource.setPassword(prop.getProperty("password"));dataSource.setDriverClassName(prop.getProperty("driverClassName"));dataSource.setUrl(prop.getProperty("url"));dataSource.setInitialSize(Integer.valueOf(prop.getProperty("initialSize")));dataSource.setMaxActive(Integer.valueOf(prop.getProperty("maxActive")));dataSource.setMaxIdle(Integer.valueOf(prop.getProperty("maxIdle")));dataSource.setRemoveAbandoned(Boolean.valueOf(prop.getProperty("removeAbandoned")));dataSource.setRemoveAbandonedTimeout(Integer.valueOf(prop.getProperty("removeAbandonedTimeout")));dataSource.setLogAbandoned(Boolean.valueOf(prop.getProperty("logAbandoned")));dataSource.setMaxWait(Integer.valueOf(prop.getProperty("maxWait")));}}}returndataSource;}catch(Exceptione){logger.error("根据数据库名数据库资源失败,",e);thrownewException("根据数据库名获取数据库资源失败");}}/***使用连接池返回一个连接对象**@return*@throwsSQLException*/publicstaticConnectiongetConnection()throwsException{try{Connectioncon=tl.get();//当con不等于null时,说明beginTransaction()有被调用,说明是开启事务!if(con!=null)returncon;returngetDataSource().getConnection();}catch(Exceptione){logger.error("获取数据库连接失败!",e);thrownewSQLException("获取数据库连接失败!");}}/***开启事务1.获取一个Connection,设置其setAutoCommit(false)*2。还要确保dao中使用的连接是我们刚刚创建的连接!--------------*3。创建一个Connection并设置为手动提交*4。将此连接用于dao!*5.使commitTransaction或rollbackTransaction可用!**@throwsSQLException*/publicstaticvoidbeginTransaction()throwsException{try{Connectioncon=tl.get();if(con!=null){con.close();tl.remove();//thrownewSQLException("一个事务已经被打开了,不要再打开了!");}con=getConnection();con.setAutoCommit(false);tl.set(con);}catch(Exceptione){logger.error("打开数据库事务失败!",e);thrownewSQLException("Databasetransactionfailedtoopen!");}}/***Committransaction1.获取beginTransaction提供的Connection,然后调用commit方法**@throwsSQLException*/publicstaticvoidcommitTransaction()throwsSQLException{Connectioncon=tl.get();try{if(con==null)thrownewSQLException("事务还没有打开,不能提交!");con.commit();}catch(异常){记录器。error("数据库事务提交失败!",e);thrownewSQLException("数据库事务提交失败!");}finally{if(con!=null){con.close();}tl.remove();}}/***回滚事务1.获取beginTransaction提供的Connection,然后调用回滚方法**@throwsSQLException*/publicstaticvoidrollbackTransaction()throwsSQLException{Connectioncon=tl.get();try{if(con==null)thrownewSQLException("事务还没有开启,不能回滚!");con.rollback();}catch(Exceptione){logger.error("数据库事务回滚失败!",e);thrownewSQLException("数据库事务回滚失败!");}finally{if(con!=null){con.close();}tl.remove();}}/***释放连接*@paramconnection*@throwsSQLException*/publicstaticvoidreleaseConnection(Connectionconnection)throwsSQLException{try{Connectioncon=tl.get();//判断是否专用于事务,如果是则不要关闭!如果它不专用于交易,那么它必须关闭!//如果con==null,说明现在没有事务,那么这个连接肯定不是事务专用的!//如果con!=null,表示有交易,那么需要判断参数connection是否与con连接等于,如果不等于,说明参数connection不是事务特定的连接if(con==null||con!=connection)connection.close();}catch(Exceptione){logger.error("数据库连接releasefailed!",e);thrownewSQLException("Databaseconnectionreleasefailed!");}}}最后编写单元测试程序DBCPTestpublicclassDBCPTest{privatestaticfinalintsumCount=1000000;privatestaticfinalintthreadNum=600;privatevoidbefore(Stringpath){SysConstants.putValue(path);newDBCPService().insert("deletefromt_test");}@TestpublicvoidtestMysql(){longstart=System.currentTimeMillis();Stringpath="配置/mysql/dbcp-jdbc.properties";before(path);for(inti=0;i<1;i++){Stringsql="insertintot_test(id,name)values('"+i+"','dbcp-mysql-"+i+"')";newDBCPService().insert(sql);}System.out.println("写入时间:"+(System.currentTimeMillis()-start));}@TestpublicvoidtestThreadMysql()throwsInterruptedException{Stringpath="config/mysql/dbcp-jdbc.properties";before(path);BlockingQueuequeue=newLinkedBlockingQueue();for(inti=0;i=druid>c3p0从角度数据库性能:oracle>postgresql>mysql,druid支持postgresql最好,c3p0性能差!3.2插入100万条数据可能会有同学们不太认同,那我们测试一下插入100万条数据,看看表现如何?测试dbcp的执行结果测试c3p0的执行结果测试druid的执行结果从上面的测试结果,我们基本可以得出以下结论:从数据连接池性能来看:Druid性能比较稳定,无论是dbcp还是c3p0有一定程度的执行失败从数据库性能来看:postgresql>oracle>mysql还是一样的结论,druid支持postgresql性能最好,c3p0的性能比较差!4、小结从上面的测试结果可以明显看出,在数据连接池方面,druid和dbcp势均力敌,在并发方面,druid比dbcp稳定,c3p0比druid和dbcp稳定,稳定性和执行速度较弱。数据库方面,postgresql比oracle快,而且oracle在各种数据源的支持和稳定性上有优势。与oracle和postgresql相比,mysql的执行速度较弱。实际开发中,数据源连接池推荐使用druid,数据库选择使用postgresql>oracle>mysql。