<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在項(xiàng)目中使用c3p0作為數(shù)據(jù)庫連接池,被技術(shù)總監(jiān)懟了

          共 6759字,需瀏覽 14分鐘

           ·

          2021-10-15 19:13

          一、介紹

          數(shù)據(jù)庫連接是一項(xiàng)非常關(guān)鍵的、有限的、昂貴的資源,這一點(diǎn)在多用戶的網(wǎng)頁應(yīng)用程序中體現(xiàn)得尤為突出。

          記得之前做的一個(gè)項(xiàng)目,當(dāng)時(shí)的應(yīng)用程序配置的c3p0數(shù)據(jù)庫連接池,最大允許的連接數(shù)是500,結(jié)果上線沒多久,并發(fā)量直接上來了,導(dǎo)致大量的數(shù)據(jù)插入失敗,當(dāng)晚的心情可想而知~

          從那一次事故之后,讓我對(duì)應(yīng)用程序的數(shù)據(jù)庫連接數(shù)有了一次深刻的認(rèn)識(shí),為了防止再次栽跟頭,特意抽了一個(gè)時(shí)間來編寫程序測(cè)試案例,用于測(cè)試各個(gè)數(shù)據(jù)源連接池的穩(wěn)定性,以防止自己再次踩坑!

          話不多說,直接擼起來!

          二、程序?qū)嵗?/span>

          熟悉 web 系統(tǒng)開發(fā)的同學(xué),基本都知道,在 Java 生態(tài)中開源的常用數(shù)據(jù)庫連接池有以下幾種:

          • dbcpDBCP是一個(gè)依賴Jakarta commons-pool對(duì)象池機(jī)制的數(shù)據(jù)庫連接池,DBCP可以直接的在應(yīng)用程序中使用,Tomcat的數(shù)據(jù)源使用的就是DBCP
          • c3p0c3p0是一個(gè)開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發(fā)布,包括了實(shí)現(xiàn)jdbc3jdbc2擴(kuò)展規(guī)范說明的ConnectionStatement池的DataSources對(duì)象
          • druid:阿里出品,淘寶和支付寶專用數(shù)據(jù)庫連接池,但它不僅僅是一個(gè)數(shù)據(jù)庫連接池,它還包含一個(gè)ProxyDriver,一系列內(nèi)置的JDBC組件庫,一個(gè)SQL Parser。支持所有JDBC兼容的數(shù)據(jù)庫,包括Oracle、MySql、Derby、Postgresql、SQL ServerH2等等。

          今天我們就一起來對(duì)比一下,這三種數(shù)據(jù)源連接池的穩(wěn)定性。

          2.1、創(chuàng)建測(cè)試表

          下面以 mysql 數(shù)據(jù)庫為例,首先創(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?ThreadLocal?tl?=?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ù)庫名稱獲取數(shù)據(jù)庫資源失敗,"?,?e);
          ???throw?new?Exception("根據(jù)數(shù)據(jù)庫名稱獲取數(shù)據(jù)庫資源失敗");
          ??}
          ?}
          ?
          ?/**
          ??*?使用連接池返回一個(gè)連接對(duì)象
          ??*?
          ??*?@return
          ??*?@throws?SQLException
          ??*/

          ?public?static?Connection?getConnection()?throws?Exception?{
          ??try?{
          ???Connection?con?=?tl.get();
          ???//?當(dāng)con不等于null,說明已經(jīng)調(diào)用過beginTransaction(),表示開啟了事務(wù)!
          ???if?(con?!=?null)
          ????return?con;
          ???return?getDataSource().getConnection();
          ??}?catch?(Exception?e)?{
          ???logger.error("獲取數(shù)據(jù)庫連接失?。?,?e);
          ???throw?new?SQLException("獲取數(shù)據(jù)庫連接失??!");
          ??}
          ?}
          ?
          ?/**
          ??*?開啟事務(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ù)庫事物開啟失??!",?e);
          ???throw?new?SQLException("數(shù)據(jù)庫事物開啟失??!");
          ??}
          ?}

          ?/**
          ??*?提交事務(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("還沒有開啟事務(wù),不能提交!");
          ???con.commit();
          ??}?catch?(Exception?e)?{
          ???logger.error("數(shù)據(jù)庫事物提交失??!",?e);
          ???throw?new?SQLException("數(shù)據(jù)庫事物提交失?。?);
          ??}?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("還沒有開啟事務(wù),不能回滾!");
          ???con.rollback();
          ??}?catch?(Exception?e)?{
          ???logger.error("數(shù)據(jù)庫事物回滾失??!",?e);
          ???throw?new?SQLException("數(shù)據(jù)庫事物回滾失??!");
          ??}?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,說明現(xiàn)在沒有事務(wù),那么connection一定不是事務(wù)專用的!
          ???//如果con?!=?null,說明有事務(wù),那么需要判斷參數(shù)連接是否與con相等,若不等,說明參數(shù)連接不是事務(wù)專用連接
          ???if?(con?==?null?||?con?!=?connection)
          ????connection.close();
          ??}?catch?(Exception?e)?{
          ???logger.error("數(shù)據(jù)庫連接釋放失?。?,?e);
          ???throw?new?SQLException("數(shù)據(jù)庫連接釋放失??!");
          ??}
          ?}

          }

          最后,編寫單元測(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);
          ??BlockingQueue?queue?=?new?LinkedBlockingQueue();
          ??for?(int?i?=?0;?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????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ù)介紹了!

          三、性能測(cè)試

          程序編寫完成之后,下面我們就一起來結(jié)合各種不同的場(chǎng)景來測(cè)試一下各個(gè)數(shù)據(jù)連接池的表現(xiàn)。

          為了進(jìn)一步擴(kuò)大測(cè)試范圍,本次測(cè)試還將各個(gè)主流的數(shù)據(jù)庫也拉入進(jìn)去,測(cè)試的數(shù)據(jù)庫分別是:mysql-5.7、oracle-12postgresql-9.6

          3.1、插入10萬條數(shù)據(jù)

          首先,我們來測(cè)試一下,各個(gè)數(shù)據(jù)庫插入10萬條數(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ù)庫性能角度看:oracle?>?postgresql?>?mysql

          其中druid對(duì)postgresql的支持性能最好,c3p0的表現(xiàn)比較差!

          3.2、插入100萬條數(shù)據(jù)

          可能有的同學(xué),還不太認(rèn)可,下面我們就來測(cè)試一下插入100萬條,看看它們的表現(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ù)庫性能角度看:postgresql?>?oracle?>?mysql

          還是一樣的結(jié)論,druid對(duì)postgresql的支持性能最好,c3p0的表現(xiàn)比較差!

          四、小結(jié)

          從上面的測(cè)試結(jié)果,我們可以很清晰的看到,在數(shù)據(jù)連接池方面,druiddbcp旗鼓相當(dāng),而并發(fā)方面druid的穩(wěn)定性大于dbcp,c3p0相比druiddbcp,穩(wěn)定性和執(zhí)行速度要弱些。

          在數(shù)據(jù)庫方面,postgresql速度要優(yōu)于oracle,而oracle對(duì)各個(gè)數(shù)據(jù)源的支持和穩(wěn)定性要有優(yōu)勢(shì),mysql相比oraclepostgresql,執(zhí)行速度要弱些。

          如果在實(shí)際開發(fā)中,數(shù)據(jù)源連接池推薦采用druid,數(shù)據(jù)庫的選用方面?postgresql?>?oracle?>?mysql

          本次測(cè)試的數(shù)據(jù)全部都是真實(shí)的,希望能幫到大家,喜歡就幫忙點(diǎn)個(gè)贊吧??


          往期推薦



          說下你可能沒用過的EventBus,再順便送幾本書好了


          沒想到,為了一個(gè)限流我寫了1萬字!


          索引下推,這個(gè)點(diǎn)你肯定不知道!


          拿捏!隔離級(jí)別、幻讀、Gap Lock、Next-Key Lock



          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩做爱视频 | 99精品视频在线 | 欧美国家一级a片 | 国产精品精品国产婷婷这里Aⅴ | 黄色视频在线观看网站 |