自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

[[398951]]


本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)公眾號。

 一、介紹

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

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

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

話不多說,直接擼起來!

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

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

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

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

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

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

  1. CREATE TABLE t_test ( 
  2.   id bigint(20) unsigned NOT NULL COMMENT '主鍵ID'
  3.   name varchar(32) NOT NULL COMMENT '名稱'
  4.   PRIMARY KEY (id) 
  5. ) ENGINE=InnoDB COMMENT='測試表'

2.2、 編寫測試用例

以dbcp為例,首先創(chuàng)建一個dbcp-jdbc.properties配置文件。

  1. username=root 
  2. password=Hello@123456 
  3. driverClassName=com.mysql.jdbc.Driver 
  4. url=jdbc:mysql://192.168.31.200:3306/testdb?useUnicode=true&characterEncoding=UTF-8 
  5. initialSize=5 
  6. maxActive=1000 
  7. maxIdle=5 
  8. removeAbandoned=ture 
  9. removeAbandonedTimeout=20 
  10. logAbandoned=true 
  11. maxWait=100 

接著,創(chuàng)建一個連接池工具DbcpJdbcUtil。

  1. public class DbcpJdbcUtil { 
  2.   
  3.  private static final Logger logger = LoggerFactory.getLogger(DbcpJdbcUtil.class); 
  4.   
  5.  /**jdbc配置文件*/ 
  6.  private static Properties prop = new Properties();  
  7.   
  8.  private static BasicDataSource dataSource = null
  9.  // 它是事務(wù)專用連接! 
  10.  private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); 
  11.   
  12.  static { 
  13.   classPathSourceRead(); 
  14.  } 
  15.  
  16.     private static void classPathSourceRead(){ 
  17.      //讀取指定位置的配置文檔(讀取class目錄文件) 
  18.      try { 
  19.       logger.info("jdbc路徑:" + SysConstants.getValue()); 
  20.    prop.load(DbcpJdbcUtil.class.getClassLoader().getResourceAsStream(SysConstants.getValue())); 
  21.    logger.info("數(shù)據(jù)配置信息" + JSON.toJSONString(prop)); 
  22.    logger.info("初始化默認(rèn)jdbc配置文件成功!"); 
  23.   } catch (Exception e) { 
  24.    logger.error("初始化默認(rèn)jdbc文件失敗!",e); 
  25.   } 
  26.     } 
  27.      
  28.  /** 
  29.   * 從連接池獲取數(shù)據(jù)源 
  30.   * @return 
  31.   * @throws Exception 
  32.   */ 
  33.  public static BasicDataSource getDataSource() throws Exception { 
  34.   try { 
  35.    if (dataSource == null) { 
  36.     synchronized (DbcpJdbcUtil.class) { 
  37.      if (dataSource == null) { 
  38.       dataSource = new BasicDataSource(); 
  39.       dataSource.setUsername(prop.getProperty("username")); 
  40.       dataSource.setPassword(prop.getProperty("password")); 
  41.       dataSource.setDriverClassName(prop.getProperty("driverClassName")); 
  42.       dataSource.setUrl(prop.getProperty("url")); 
  43.       dataSource.setInitialSize(Integer.valueOf(prop.getProperty("initialSize"))); 
  44.       dataSource.setMaxActive(Integer.valueOf(prop.getProperty("maxActive"))); 
  45.       dataSource.setMaxIdle(Integer.valueOf(prop.getProperty("maxIdle"))); 
  46.       dataSource.setRemoveAbandoned(Boolean.valueOf(prop.getProperty("removeAbandoned"))); 
  47.       dataSource.setRemoveAbandonedTimeout(Integer.valueOf(prop.getProperty("removeAbandonedTimeout"))); 
  48.       dataSource.setLogAbandoned(Boolean.valueOf(prop.getProperty("logAbandoned"))); 
  49.       dataSource.setMaxWait(Integer.valueOf(prop.getProperty("maxWait"))); 
  50.      } 
  51.     } 
  52.    } 
  53.    return dataSource; 
  54.   } catch (Exception e) { 
  55.    logger.error("根據(jù)數(shù)據(jù)庫名稱獲取數(shù)據(jù)庫資源失敗," , e); 
  56.    throw new Exception("根據(jù)數(shù)據(jù)庫名稱獲取數(shù)據(jù)庫資源失敗"); 
  57.   } 
  58.  } 
  59.   
  60.  /** 
  61.   * 使用連接池返回一個連接對象 
  62.   *  
  63.   * @return 
  64.   * @throws SQLException 
  65.   */ 
  66.  public static Connection getConnection() throws Exception { 
  67.   try { 
  68.    Connection con = tl.get(); 
  69.    // 當(dāng)con不等于null,說明已經(jīng)調(diào)用過beginTransaction(),表示開啟了事務(wù)! 
  70.    if (con != null
  71.     return con; 
  72.    return getDataSource().getConnection(); 
  73.   } catch (Exception e) { 
  74.    logger.error("獲取數(shù)據(jù)庫連接失??!", e); 
  75.    throw new SQLException("獲取數(shù)據(jù)庫連接失敗!"); 
  76.   } 
  77.  } 
  78.   
  79.  /** 
  80.   * 開啟事務(wù) 1. 獲取一個Connection,設(shè)置它的setAutoComnmit(false)  
  81.   * 2. 還要保證dao中使用的連接是我們剛剛創(chuàng)建的! --------------  
  82.   * 3. 創(chuàng)建一個Connection,設(shè)置為手動提交  
  83.   * 4. 把這個Connection給dao用!  
  84.   * 5. 還要讓commitTransaction或rollbackTransaction可以獲取到! 
  85.   *  
  86.   * @throws SQLException 
  87.   */ 
  88.  public static void beginTransaction() throws Exception { 
  89.   try { 
  90.    Connection con = tl.get(); 
  91.    if (con != null) { 
  92.     con.close(); 
  93.     tl.remove(); 
  94.     //throw new SQLException("已經(jīng)開啟了事務(wù),就不要重復(fù)開啟了!"); 
  95.    } 
  96.    con = getConnection(); 
  97.    con.setAutoCommit(false); 
  98.    tl.set(con); 
  99.   } catch (Exception e) { 
  100.    logger.error("數(shù)據(jù)庫事物開啟失?。?quot;, e); 
  101.    throw new SQLException("數(shù)據(jù)庫事物開啟失??!"); 
  102.   } 
  103.  } 
  104.  
  105.  /** 
  106.   * 提交事務(wù) 1. 獲取beginTransaction提供的Connection,然后調(diào)用commit方法 
  107.   *  
  108.   * @throws SQLException 
  109.   */ 
  110.  public static void commitTransaction() throws SQLException { 
  111.   Connection con = tl.get(); 
  112.   try { 
  113.    if (con == null
  114.     throw new SQLException("還沒有開啟事務(wù),不能提交!"); 
  115.    con.commit(); 
  116.   } catch (Exception e) { 
  117.    logger.error("數(shù)據(jù)庫事物提交失??!", e); 
  118.    throw new SQLException("數(shù)據(jù)庫事物提交失敗!"); 
  119.   } finally { 
  120.    if (con != null) { 
  121.     con.close(); 
  122.    } 
  123.    tl.remove(); 
  124.   } 
  125.  } 
  126.   
  127.  /** 
  128.   * 回滾事務(wù) 1. 獲取beginTransaction提供的Connection,然后調(diào)用rollback方法 
  129.   *  
  130.   * @throws SQLException 
  131.   */ 
  132.  public static void rollbackTransaction() throws SQLException { 
  133.   Connection con = tl.get(); 
  134.   try { 
  135.    if (con == null
  136.     throw new SQLException("還沒有開啟事務(wù),不能回滾!"); 
  137.    con.rollback(); 
  138.   } catch (Exception e) { 
  139.    logger.error("數(shù)據(jù)庫事物回滾失?。?quot;, e); 
  140.    throw new SQLException("數(shù)據(jù)庫事物回滾失??!"); 
  141.   } finally { 
  142.    if (con != null) { 
  143.     con.close(); 
  144.    } 
  145.    tl.remove(); 
  146.   } 
  147.  } 
  148.   
  149.  /** 
  150.   * 釋放連接  
  151.   * @param connection 
  152.   * @throws SQLException 
  153.   */ 
  154.  public static void releaseConnection(Connection connection) throws SQLException { 
  155.   try { 
  156.    Connection con = tl.get(); 
  157.    // 判斷它是不是事務(wù)專用,如果是,就不關(guān)閉! 如果不是事務(wù)專用,那么就要關(guān)閉! 
  158.    // 如果con == null,說明現(xiàn)在沒有事務(wù),那么connection一定不是事務(wù)專用的! 
  159.    //如果con != null,說明有事務(wù),那么需要判斷參數(shù)連接是否與con相等,若不等,說明參數(shù)連接不是事務(wù)專用連接 
  160.    if (con == null || con != connection
  161.     connection.close(); 
  162.   } catch (Exception e) { 
  163.    logger.error("數(shù)據(jù)庫連接釋放失敗!", e); 
  164.    throw new SQLException("數(shù)據(jù)庫連接釋放失??!"); 
  165.   } 
  166.  } 
  167.  

最后,編寫單元測試程序DBCPTest。

  1. public class DBCPTest { 
  2.   
  3.  private static final int sumCount = 1000000; 
  4.   
  5.  private static final int threadNum = 600; 
  6.   
  7.  private void before(String path) { 
  8.   SysConstants.putValue(path); 
  9.   new DBCPService().insert("delete from t_test"); 
  10.  } 
  11.   
  12.  @Test 
  13.  public void testMysql() { 
  14.   long start = System.currentTimeMillis(); 
  15.   String path = "config/mysql/dbcp-jdbc.properties"
  16.   before(path); 
  17.   for (int i =0; i < 1; i++) { 
  18.    String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')"
  19.    new DBCPService().insert(sql); 
  20.   } 
  21.   System.out.println("耗時:" + (System.currentTimeMillis() - start)); 
  22.  } 
  23.   
  24.  @Test 
  25.  public void testThreadMysql() throws InterruptedException { 
  26.   String path = "config/mysql/dbcp-jdbc.properties"
  27.   before(path); 
  28.   BlockingQueue<String> queue = new LinkedBlockingQueue<String>(); 
  29.   for (int i = 0; i < sumCount; i++) { 
  30.    String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')"
  31.    queue.put(sql); 
  32.   } 
  33.   long start = System.currentTimeMillis(); 
  34.   final CountDownLatch countDownLatch = new CountDownLatch(threadNum); 
  35.   for (int i = 0; i < threadNum; i++) { 
  36.    final int finalI = i + 1; 
  37.    new Thread(new Runnable() { 
  38.     @Override 
  39.     public void run() { 
  40.      System.out.println("thread " + finalI + " start"); 
  41.      boolean isGo = true
  42.      while (isGo) { 
  43.       String sql = queue.poll(); 
  44.       if(sql != null) { 
  45.        new DBCPService().insert(sql); 
  46.       }else { 
  47.        isGo =false
  48.        System.out.println("thread " + finalI + " finish"); 
  49.        countDownLatch.countDown(); 
  50.       } 
  51.      } 
  52.     } 
  53.    }).start(); 
  54.   } 
  55.   countDownLatch.await();  
  56.   System.out.println("耗時:" + (System.currentTimeMillis() - start)); 
  57.  } 
  58.  

c3p0、druid的配置也類似,這里就不在重復(fù)介紹了!

三、性能測試

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

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

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

首先,我們來測試一下,各個數(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對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)定,dbcp、c3p0都有某種程度的執(zhí)行失敗
  • 從數(shù)據(jù)庫性能角度看:postgresql > oracle > mysql

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

四、小結(jié)

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

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

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

 

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2015-10-29 16:59:47

數(shù)據(jù)庫

2021-04-12 07:32:01

數(shù)據(jù)庫

2018-07-20 14:50:43

Java數(shù)據(jù)庫連接池

2009-09-22 17:53:09

Hibernate C

2020-03-04 13:55:28

c3p0數(shù)據(jù)庫連接池

2009-07-15 11:14:30

c3p0連接池

2009-09-22 14:44:18

Hibernate.c

2013-06-17 10:25:16

連接池Java

2019-11-27 10:31:51

數(shù)據(jù)庫連接池內(nèi)存

2010-03-18 15:09:15

python數(shù)據(jù)庫連接

2009-06-24 07:53:47

Hibernate數(shù)據(jù)

2015-04-27 09:50:45

Java Hibern連接池詳解

2022-07-19 13:51:47

數(shù)據(jù)庫Hikari連接池

2009-06-17 09:34:31

Hibernate3Hibernate2連接池

2009-08-10 17:34:42

C#數(shù)據(jù)庫連接池

2017-06-22 14:13:07

PythonMySQLpymysqlpool

2009-06-16 09:25:31

JBoss配置

2009-09-22 14:52:55

Hibernate p

2018-10-10 14:27:34

數(shù)據(jù)庫連接池MySQL

2020-04-30 14:38:51

數(shù)據(jù)庫連接池線程
點贊
收藏

51CTO技術(shù)棧公眾號