<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>

          為什么說Druid是目前最好的數(shù)據(jù)庫連接池?

          共 13373字,需瀏覽 27分鐘

           ·

          2022-07-25 05:44

          一、介紹

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

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

          從那一次事故之后,讓我對(duì)應(yīng)用程序的數(shù)據(jù)庫連接數(shù)有了一次深刻的認(rèn)識(shí),為了防止再次栽跟頭,之后特意抽了一個(gè)時(shí)間來編寫程序測試案例,用于測試各個(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ù)庫,包括OracleMySqlDerbyPostgresqlSQL ServerH2等等。

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

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

          下面以 mysql 數(shù)據(jù)庫為例,創(chuàng)建一個(gè)t_test表,方面后續(xù)進(jìn)行插入數(shù)據(jù)操作。

          CREATE TABLE t_test (
            id bigint(20unsigned NOT NULL COMMENT '主鍵ID',
            name varchar(32NOT NULL COMMENT '名稱',
            PRIMARY KEY (id)
          ENGINE=InnoDB COMMENT='測試表';

          2.2、 編寫測試用例

          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<Connection> tl = new ThreadLocal<Connection>();
           
           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ù)庫連接釋放失敗!");
            }
           }

          }

          最后,編寫單元測試程序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<String> queue = new LinkedBlockingQueue<String>();
            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ù)介紹了!

          三、性能測試

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

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

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

          首先,我們來測試一下,各個(gè)數(shù)據(jù)庫插入10萬條數(shù)據(jù),采用不同的數(shù)據(jù)源連接池,看看它們的表現(xiàn)如何?

          • 測試dbcp執(zhí)行結(jié)果
          • 測試c3p0執(zhí)行結(jié)果
          • 測試druid執(zhí)行結(jié)果

          從上面測試結(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)可,下面我們就來測試一下插入100萬條,看看它們的表現(xiàn)如何?

          • 測試dbcp執(zhí)行結(jié)果
          • 測試c3p0執(zhí)行結(jié)果
          • 測試druid執(zhí)行結(jié)果

          從上面測試結(jié)果,我們可以基本得出如下結(jié)論:

          • 從數(shù)據(jù)連接池性能角度看:druid性能比較穩(wěn)定,dbcpc3p0都有某種程度的執(zhí)行失敗
          • 從數(shù)據(jù)庫性能角度看:postgresql > oracle > mysql

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

          四、小結(jié)

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

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

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


          程序汪資料鏈接

          程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

          Java項(xiàng)目分享  最新整理全集,找項(xiàng)目不累啦 07版

          堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門到實(shí)戰(zhàn)進(jìn)階

          臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開放下載!


          歡迎添加程序汪個(gè)人微信 itwang009  進(jìn)粉絲群或圍觀朋友圈

          瀏覽 51
          點(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>
                  日韩av在线小说 日韩av在线直播 | 亚洲日韩欧美色图 | 黄色一级特黄大片 | 欧美性18 | 青青草乱伦视频 |