1.介绍数据库连接是一种非常关键、有限且昂贵的资源,尤其是在多用户Web应用程序中。我记得我以前做过的一个项目。当时应用配置的数据库连接池允许最大连接数为500,结果上线没多久,并发量直接增加,导致大量数据插入失败。那天晚上的心情可想而知~那次意外之后,我对应用中的数据库连接数有了深刻的认识。为了防止再被绊倒,我专门花时间写了一个程序测试用例来测试各个数据源的连接池的稳定性。性别!话不多说,直接卷起来!2、程序示例熟悉web系统开发的同学基本都知道,Java生态中常见的开源数据库连接池有以下几种:dbcp:DBCP是一个依赖于Jakartacommons-pool对象的数据库连接池pool机制,在应用程序中可以直接使用DBCP,Tomcat的数据源使用DBCP。c3p0:c3p0是一个开源的JDBC连接池,与Hibernate一起发布在lib目录下,包括实现jdbc3和jdbc2扩展规范中描述的Connection和Statement池的DataSources对象。druid:阿里、淘宝、支付宝出品的专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含了一个ProxyDriver,一系列内置的JDBC组件库,以及一个SQLParser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQLServer、H2等。今天我们就将这三种数据源连接池的稳定性放在一起进行比较。2.1.创建测试表下面以mysql数据库为例,创建一张t_test表,用于后续的数据插入操作。CREATETABLEt_test(idbigint(20)unsignedNOTNULLCOMMENT'primarykeyID',namevarchar(32)NOTNULLCOMMENT'name',PRIMARYKEY(id))ENGINE=InnoDBCOMMENT='testtable';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。公共类DbcpJdbcUtil{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(DbcpJdbcUtil.class);/**jdbc配置文件*/privatestaticPropertiesprop=newProperties();私有静态BasicDataSource数据源=null;//它是仅事务连接!privatestaticThreadLocaltl=newThreadLocal();静态{classPathSourceRead();}privatestaticvoidclassPathSourceRead(){//读取指定位置的配置文件(读取类目录文件)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("用户名"));dataSource.setPassword(prop.getProperty("密码"));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")));}}}返回数据源;}赶上(异常e){记录器。error("根据数据库名获取数据库资源失败,",e);thrownewException("根据数据库名获取数据库资源失败");}}/***使用连接池返回一个连接对象**@return*@throwsSQLException*/publicstaticConnectiongetConnection()throwsException{try{Connectioncon=tl.get();//当con不等于null时,表示已经调用了beginTransaction(),表示事务已经开始!如果(con!=null)返回con;返回getDataSource().getConnection();}catch(Exceptione){logger.error("获取数据库连接失败!",e);thrownewSQLException("获取数据库连接失败!");}}/***打开事务1.获取一个Connection并设置它的setAutoCommit(false)*2.还要确保dao中使用的connection是我们刚刚创建的!--------------*3.创建一个Connection并设置为手动提交*4.将这个Connection用于dao!*5.使commitTransaction或rollbackTransaction可用!**@throwsSQLException*/publicstaticvoidbeginTransaction()throwsException{try{Connectioncon=tl.get();if(con!=null){con.close();tl.remove();//thrownewSQLException("事务已经打开,请勿再次打开!");}con=getConnection();con.setAutoCommi吨(假);tl.set(con);}catch(Exceptione){logger.error("无法打开数据库事务!",e);thrownewSQLException("无法打开数据库事务!");}}/***Committransaction1.获取beginTransaction提供的Connection,然后调用commit方法**@throwsSQLException*/publicstaticvoidcommitTransaction()throwsSQLException{Connectioncon=tl.get();try{if(con==null)thrownewSQLException("事务还不能提交!");提交();}catch(Exceptione){logger.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("事务还没有打开,不能回滚!");控制回滚();}catch(Exceptione){logger.error("数据库事务回滚失败!",e);thrownewSQLException("数据库事务回滚失败!");}最后{如果(con!=null){con.close();}tl.remove();}}/***释放连接*@paramconnection*@throwsSQLException*/publicstaticvoidreleaseConnection(Connectionconnection)throwsSQLException{try{Connectioncon=tl.get();//判断是否专用于交易,如果是,不要关闭!如果它不专用于交易,那么它必须关闭!//如果con==null,表示现在没有事务,所以连接不能专用于事务!//如果con!=null,说明有事务,那么需要判断参数connection是否等于con,如果不等于,说明参数connection不是事务专用连接if(con==空||con!=connection)connection.close();}catch(Exceptione){logger.error("释放数据库连接失败!",e);thrownewSQLException("释放数据库连接失败!");}}}最后编写单元测试程序DBCPTestpublicclassDBCPTest{privatestaticfinalintsumCount=1000000;privatestaticfinalintthreadNum=600;privatevoidbefore(Stringpath){SysConstants.putValue(path);newDBCPService().insert("从t_test中删除");}@TestpublicvoidtestMysql(){longstart=System.currentTimeMillis();Stringpath="config/mysql/dbcp-jdbc.properties";之前(路径);for(inti=0;i<1;i++){Stringsql="insertintot_test(id,name)values('"+i+"','dbcp-mysql-"+i+"')";}新的DBCPService().insert(sql);}System.out.println("超时:"+(System.currentTimeMillis()-start));}@TestpublicvoidtestThreadMysql()throwsInterruptedException{Stringpath="config/mysql/dbcp-jdbc.properties";之前(路径);BlockingQueuequeue=newLinkedBlockingQueue();for(inti=0;idruid>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。