這篇文章主要講解了“數(shù)據(jù)庫(kù)連接池的方式有哪幾種”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“數(shù)據(jù)庫(kù)連接池的方式有哪幾種”吧!
專業(yè)成都網(wǎng)站建設(shè)公司,做排名好的好網(wǎng)站,排在同行前面,為您帶來(lái)客戶和效益!創(chuàng)新互聯(lián)為您提供成都網(wǎng)站建設(shè),五站合一網(wǎng)站設(shè)計(jì)制作,服務(wù)好的網(wǎng)站設(shè)計(jì)公司,網(wǎng)站建設(shè)、網(wǎng)站制作負(fù)責(zé)任的成都網(wǎng)站制作公司!
數(shù)據(jù)庫(kù)連接是一項(xiàng)非常關(guān)鍵的、有限的、昂貴的資源,這一點(diǎn)在多用戶的網(wǎng)頁(yè)應(yīng)用程序中體現(xiàn)得尤為突出。
記得之前做的一個(gè)項(xiàng)目,當(dāng)時(shí)的應(yīng)用程序配置的c3p0數(shù)據(jù)庫(kù)連接池,最大允許的連接數(shù)是500,結(jié)果上線沒(méi)多久,并發(fā)量直接上來(lái)了,導(dǎo)致大量的數(shù)據(jù)插入失敗,當(dāng)晚的心情可想而知~
從那一次事故之后,讓我對(duì)應(yīng)用程序的數(shù)據(jù)庫(kù)連接數(shù)有了一次深刻的認(rèn)識(shí),為了防止再次栽跟頭,特意抽了一個(gè)時(shí)間來(lái)編寫程序測(cè)試案例,用于測(cè)試各個(gè)數(shù)據(jù)源連接池的穩(wěn)定性,以防止自己再次踩坑!
話不多說(shuō),直接擼起來(lái)!
熟悉 web 系統(tǒng)開發(fā)的同學(xué),基本都知道,在 Java 生態(tài)中開源的常用數(shù)據(jù)庫(kù)連接池有以下幾種:
dbcp:DBCP是一個(gè)依賴Jakarta commons-pool對(duì)象池機(jī)制的數(shù)據(jù)庫(kù)連接池,DBCP可以直接的在應(yīng)用程序中使用,Tomcat的數(shù)據(jù)源使用的就是DBCP
c3p0:c3p0是一個(gè)開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發(fā)布,包括了實(shí)現(xiàn)jdbc3和jdbc2擴(kuò)展規(guī)范說(shuō)明的Connection和Statement池的DataSources對(duì)象
druid:阿里出品,淘寶和支付寶專用數(shù)據(jù)庫(kù)連接池,但它不僅僅是一個(gè)數(shù)據(jù)庫(kù)連接池,它還包含一個(gè)ProxyDriver,一系列內(nèi)置的JDBC組件庫(kù),一個(gè)SQL Parser。支持所有JDBC兼容的數(shù)據(jù)庫(kù),包括Oracle、MySQL、Derby、Postgresql、SQL Server、H2等等。
今天我們就一起來(lái)對(duì)比一下,這三種數(shù)據(jù)源連接池的穩(wěn)定性。
2.1、創(chuàng)建測(cè)試表
下面以 mysql 數(shù)據(jù)庫(kù)為例,首先創(chuàng)建一個(gè)t_test表,方面后續(xù)進(jìn)行插入數(shù)據(jù)操作。
CREATE TABLE t_test ( id bigint(20) unsigned NOT NULL COMMENT '主鍵ID', name varchar(32) NOT NULL COMMENT '名稱', PRIMARY KEY (id) ) ENGINE=InnoDB COMMENT='測(cè)試表';
2.2、 編寫測(cè)試用例
以dbcp為例,首先創(chuàng)建一個(gè)dbcp-jdbc.properties配置文件。
username=root password=Hello@123456 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://192.168.31.200:3306/testdb?useUnicode=true&characterEncoding=UTF-8 initialSize=5 maxActive=1000 maxIdle=5 removeAbandoned=ture removeAbandonedTimeout=20 logAbandoned=true maxWait=100
接著,創(chuàng)建一個(gè)連接池工具DbcpJdbcUtil。
public class DbcpJdbcUtil { private static final Logger logger = LoggerFactory.getLogger(DbcpJdbcUtil.class); /**jdbc配置文件*/ private static Properties prop = new Properties(); private static BasicDataSource dataSource = null; // 它是事務(wù)專用連接! private static ThreadLocaltl = new ThreadLocal (); static { classPathSourceRead(); } private static void classPathSourceRead(){ //讀取指定位置的配置文檔(讀取class目錄文件) try { logger.info("jdbc路徑:" + SysConstants.getValue()); prop.load(DbcpJdbcUtil.class.getClassLoader().getResourceAsStream(SysConstants.getValue())); logger.info("數(shù)據(jù)配置信息" + JSON.toJSONString(prop)); logger.info("初始化默認(rèn)jdbc配置文件成功!"); } catch (Exception e) { logger.error("初始化默認(rèn)jdbc文件失敗!",e); } } /** * 從連接池獲取數(shù)據(jù)源 * @return * @throws Exception */ public static BasicDataSource getDataSource() throws Exception { try { if (dataSource == null) { synchronized (DbcpJdbcUtil.class) { if (dataSource == null) { dataSource = new BasicDataSource(); dataSource.setUsername(prop.getProperty("username")); 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"))); } } } return dataSource; } catch (Exception e) { logger.error("根據(jù)數(shù)據(jù)庫(kù)名稱獲取數(shù)據(jù)庫(kù)資源失敗," , e); throw new Exception("根據(jù)數(shù)據(jù)庫(kù)名稱獲取數(shù)據(jù)庫(kù)資源失敗"); } } /** * 使用連接池返回一個(gè)連接對(duì)象 * * @return * @throws SQLException */ public static Connection getConnection() throws Exception { try { Connection con = tl.get(); // 當(dāng)con不等于null,說(shuō)明已經(jīng)調(diào)用過(guò)beginTransaction(),表示開啟了事務(wù)! if (con != null) return con; return getDataSource().getConnection(); } catch (Exception e) { logger.error("獲取數(shù)據(jù)庫(kù)連接失?。?, e); throw new SQLException("獲取數(shù)據(jù)庫(kù)連接失?。?); } } /** * 開啟事務(wù) 1. 獲取一個(gè)Connection,設(shè)置它的setAutoComnmit(false) * 2. 還要保證dao中使用的連接是我們剛剛創(chuàng)建的! -------------- * 3. 創(chuàng)建一個(gè)Connection,設(shè)置為手動(dòng)提交 * 4. 把這個(gè)Connection給dao用! * 5. 還要讓commitTransaction或rollbackTransaction可以獲取到! * * @throws SQLException */ public static void beginTransaction() throws Exception { try { Connection con = tl.get(); if (con != null) { con.close(); tl.remove(); //throw new SQLException("已經(jīng)開啟了事務(wù),就不要重復(fù)開啟了!"); } con = getConnection(); con.setAutoCommit(false); tl.set(con); } catch (Exception e) { logger.error("數(shù)據(jù)庫(kù)事物開啟失??!", e); throw new SQLException("數(shù)據(jù)庫(kù)事物開啟失?。?); } } /** * 提交事務(wù) 1. 獲取beginTransaction提供的Connection,然后調(diào)用commit方法 * * @throws SQLException */ public static void commitTransaction() throws SQLException { Connection con = tl.get(); try { if (con == null) throw new SQLException("還沒(méi)有開啟事務(wù),不能提交!"); con.commit(); } catch (Exception e) { logger.error("數(shù)據(jù)庫(kù)事物提交失??!", e); throw new SQLException("數(shù)據(jù)庫(kù)事物提交失??!"); } finally { if (con != null) { con.close(); } tl.remove(); } } /** * 回滾事務(wù) 1. 獲取beginTransaction提供的Connection,然后調(diào)用rollback方法 * * @throws SQLException */ public static void rollbackTransaction() throws SQLException { Connection con = tl.get(); try { if (con == null) throw new SQLException("還沒(méi)有開啟事務(wù),不能回滾!"); con.rollback(); } catch (Exception e) { logger.error("數(shù)據(jù)庫(kù)事物回滾失??!", e); throw new SQLException("數(shù)據(jù)庫(kù)事物回滾失??!"); } finally { if (con != null) { con.close(); } tl.remove(); } } /** * 釋放連接 * @param connection * @throws SQLException */ public static void releaseConnection(Connection connection) throws SQLException { try { Connection con = tl.get(); // 判斷它是不是事務(wù)專用,如果是,就不關(guān)閉! 如果不是事務(wù)專用,那么就要關(guān)閉! // 如果con == null,說(shuō)明現(xiàn)在沒(méi)有事務(wù),那么connection一定不是事務(wù)專用的! //如果con != null,說(shuō)明有事務(wù),那么需要判斷參數(shù)連接是否與con相等,若不等,說(shuō)明參數(shù)連接不是事務(wù)專用連接 if (con == null || con != connection) connection.close(); } catch (Exception e) { logger.error("數(shù)據(jù)庫(kù)連接釋放失??!", e); throw new SQLException("數(shù)據(jù)庫(kù)連接釋放失?。?); } } }
最后,編寫單元測(cè)試程序DBCPTest。
public class DBCPTest { private static final int sumCount = 1000000; private static final int threadNum = 600; private void before(String path) { SysConstants.putValue(path); new DBCPService().insert("delete from t_test"); } @Test public void testMysql() { long start = System.currentTimeMillis(); String path = "config/mysql/dbcp-jdbc.properties"; before(path); for (int i =0; i < 1; i++) { String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')"; new DBCPService().insert(sql); } System.out.println("耗時(shí):" + (System.currentTimeMillis() - start)); } @Test public void testThreadMysql() throws InterruptedException { String path = "config/mysql/dbcp-jdbc.properties"; before(path); BlockingQueuequeue = new LinkedBlockingQueue (); for (int i = 0; i < sumCount; i++) { String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')"; queue.put(sql); } long start = System.currentTimeMillis(); final CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) { final int finalI = i + 1; new Thread(new Runnable() { @Override public void run() { System.out.println("thread " + finalI + " start"); boolean isGo = true; while (isGo) { String sql = queue.poll(); if(sql != null) { new DBCPService().insert(sql); }else { isGo =false; System.out.println("thread " + finalI + " finish"); countDownLatch.countDown(); } } } }).start(); } countDownLatch.await(); System.out.println("耗時(shí):" + (System.currentTimeMillis() - start)); } }
c3p0、druid的配置也類似,這里就不在重復(fù)介紹了!
程序編寫完成之后,下面我們就一起來(lái)結(jié)合各種不同的場(chǎng)景來(lái)測(cè)試一下各個(gè)數(shù)據(jù)連接池的表現(xiàn)。
為了進(jìn)一步擴(kuò)大測(cè)試范圍,本次測(cè)試還將各個(gè)主流的數(shù)據(jù)庫(kù)也拉入進(jìn)去,測(cè)試的數(shù)據(jù)庫(kù)分別是:mysql-5.7、oracle-12、postgresql-9.6
3.1、插入10萬(wàn)條數(shù)據(jù)
首先,我們來(lái)測(cè)試一下,各個(gè)數(shù)據(jù)庫(kù)插入10萬(wàn)條數(shù)據(jù),采用不同的數(shù)據(jù)源連接池,看看它們的表現(xiàn)如何?
測(cè)試dbcp執(zhí)行結(jié)果
測(cè)試c3p0執(zhí)行結(jié)果
測(cè)試druid執(zhí)行結(jié)果
從上面測(cè)試結(jié)果,我們可以基本得出如下結(jié)論:
從數(shù)據(jù)連接池性能角度看:dbcp >= druid > c3p0
從數(shù)據(jù)庫(kù)性能角度看:oracle > postgresql > mysql
其中druid對(duì)postgresql的支持性能最好,c3p0的表現(xiàn)比較差!
3.2、插入100萬(wàn)條數(shù)據(jù)
可能有的同學(xué),還不太認(rèn)可,下面我們就來(lái)測(cè)試一下插入100萬(wàn)條,看看它們的表現(xiàn)如何?
測(cè)試dbcp執(zhí)行結(jié)果
測(cè)試c3p0執(zhí)行結(jié)果
測(cè)試druid執(zhí)行結(jié)果
從上面測(cè)試結(jié)果,我們可以基本得出如下結(jié)論:
從數(shù)據(jù)連接池性能角度看:druid性能比較穩(wěn)定,dbcp、c3p0都有某種程度的執(zhí)行失敗
從數(shù)據(jù)庫(kù)性能角度看:postgresql > oracle > mysql
還是一樣的結(jié)論,druid對(duì)postgresql的支持性能最好,c3p0的表現(xiàn)比較差!
從上面的測(cè)試結(jié)果,我們可以很清晰的看到,在數(shù)據(jù)連接池方面,druid和dbcp旗鼓相當(dāng),而并發(fā)方面druid的穩(wěn)定性大于dbcp,c3p0相比druid和dbcp,穩(wěn)定性和執(zhí)行速度要弱些。
在數(shù)據(jù)庫(kù)方面,postgresql速度要優(yōu)于oracle,而oracle對(duì)各個(gè)數(shù)據(jù)源的支持和穩(wěn)定性要有優(yōu)勢(shì),mysql相比oracle和postgresql,執(zhí)行速度要弱些。
如果在實(shí)際開發(fā)中,數(shù)據(jù)源連接池推薦采用druid,數(shù)據(jù)庫(kù)的選用方面 postgresql > oracle > mysql。
感謝各位的閱讀,以上就是“數(shù)據(jù)庫(kù)連接池的方式有哪幾種”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)數(shù)據(jù)庫(kù)連接池的方式有哪幾種這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!