1. 程式人生 > >主流Java資料庫連線池比較與開發配置實戰

主流Java資料庫連線池比較與開發配置實戰

1.資料庫連線池概述

資料庫連線的建立是一種耗時、效能低、代價高的操作,頻繁的資料庫連線的建立和關閉極大的影響了系統的效能。資料庫連線池是系統初始化過程中建立一定數量的資料庫連線放於連線池中,當程式需要訪問資料庫時,不再建立一個新的連線,而是從連線池中取出一個已建立的空閒連線,使用完畢後,程式將連線歸還到連線池中,供其他請求使用,從而實現的資源的共享,連線的建立、斷開都由連線池自身來管理。

資料庫連線池為系統的執行帶來了以下優勢:昂貴的資料庫連線資源得到重用;減少了資料庫連線建立和釋放的時間開銷,提高了系統響應速度;統一的資料庫連線管理,避免了連線資源的洩露。

資料庫連線池執行機制:

系統初始化時建立連線池,程式操作資料庫時從連線池中獲取空閒連線,程式使用完畢將連線歸還到連線池中,系統退出時,斷開所有資料庫連線並釋放記憶體資源。

2.主流資料庫連線池比較

常用的主流開源資料庫連線池有C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等

C3p0: 開源的JDBC連線池,實現了資料來源和JNDI繫結,支援JDBC3規範和JDBC2的標準擴充套件。目前使用它的開源專案有Hibernate、Spring等。單執行緒,效能較差,適用於小型系統,程式碼600KB左右。

DBCP (Database Connection Pool):由Apache開發的一個Java資料庫連線池專案, Jakarta commons-pool物件池機制,Tomcat使用的連線池元件就是DBCP。單獨使用dbcp需要3個包:common-dbcp.jar,common-pool.jar,common-collections.jar,預先將資料庫連線放在記憶體中,應用程式需要建立資料庫連線時直接到連線池中申請一個就行,用完再放回。單執行緒,併發量低,效能不好,適用於小型系統。

Tomcat Jdbc Pool:Tomcat在7.0以前都是使用common-dbcp做為連線池元件,但是dbcp是單執行緒,為保證執行緒安全會鎖整個連線池,效能較差,dbcp有超過60個類,也相對複雜。Tomcat從7.0開始引入了新增連線池模組叫做Tomcat jdbc pool,基於Tomcat JULI,使用Tomcat日誌框架,完全相容dbcp,通過非同步方式獲取連線,支援高併發應用環境,超級簡單核心檔案只有8個,支援JMX,支援XA Connection。

BoneCP:官方說法BoneCP是一個高效、免費、開源的Java資料庫連線池實現庫。設計初衷就是為了提高資料庫連線池效能,根據某些測試資料顯示,BoneCP的速度是最快的,要比當時第二快速的連線池快25倍左右,完美整合到一些持久化產品如Hibernate和DataNucleus中。BoneCP特色:

高度可擴充套件,快速;連線狀態切換的回撥機制;允許直接訪問連線;自動化重置能力;JMX支援;懶載入能力;支援XML和屬性檔案配置方式;較好的Java程式碼組織,100%單元測試分支程式碼覆蓋率;程式碼40KB左右。

Druid:Druid是Java語言中最好的資料庫連線池,Druid能夠提供強大的監控和擴充套件功能,是一個可用於大資料實時查詢和分析的高容錯、高效能的開源分散式系統,尤其是當發生程式碼部署、機器故障以及其他產品系統遇到宕機等情況時,Druid仍能夠保持100%正常執行。主要特色:為分析監控設計;快速的互動式查詢;高可用;可擴充套件;Druid是一個開源專案,原始碼託管在github上。

主流連線池各項功能對比如下:

3.資料庫連線池Spring整合配置與JNDI配置

下面針對每一種連線池的使用方法,在開發中如何配置給出spring整合配置和在tomcat的conf/context.xml檔案中配置2種方式,限於篇幅只給出基本引數,詳細引數可自行研究。

3.1 阿里Druid連線池

Maven依賴

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.0.28</version>
</dependency>

Spring整合配置方式

<!--Spring Druid 資料來源配置-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本屬性 url、user、password -->
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="20" />
        <!-- 配置獲取連線等待超時的時間 -->
        <property name="maxWait" value="60000" />
        <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一個連線在池中最小生存的時間,單位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <!-- 開啟PSCache,並且指定每個連線上PSCache的大小 -->
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
        <!-- 配置監控統計攔截的filters,去掉後監控介面sql無法統計 -->
        <property name="filters" value="stat" />
    </bean>
	Web.xml配置
	 <!--druid WebStatFilter用於採集web-jdbc關聯監控的資料-->
	  <filter>
		<filter-name>DruidWebStatFilter</filter-name>
		<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
		<init-param>
		  <param-name>exclusions</param-name>
		  <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
		</init-param>
	  </filter>
	  <filter-mapping>
		<filter-name>DruidWebStatFilter</filter-name>
		<url-pattern>/*</url-pattern>
	  </filter-mapping>
	  <!--druid訪問監控介面 /druid/index.html-->
	  <servlet>
		<servlet-name>DruidStatView</servlet-name>
		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
	  </servlet>
	  <servlet-mapping>
		<servlet-name>DruidStatView</servlet-name>
		<url-pattern>/druid/*</url-pattern>
	  </servlet-mapping>

Tomcat中context.xml檔案JNDI配置方式

com.alibaba.druid.pool.DruidDataSourceFactory實現了javax.naming.spi.ObjectFactory,可以作為JNDI資料來源來配置
<TOMCAT_HOME>/conf/context.xml配置JNDI方式

 <Resource
         name="jdbc/MysqlDataSource"
         factory="com.alibaba.druid.pool.DruidDataSourceFactory"
         auth="Container"
         type="javax.sql.DataSource"
         driverClassName="com.mysql.jdbc.Driver"
         url="jdbc:mysql://192.168.1.233:3306/lead_oams?useUnicode=true&characterEncoding=utf-8"
         username="lead_system"
         password="password"
         maxActive="50"
         maxWait="10000"
         removeabandoned="true"
         removeabandonedtimeout="60"
         logabandoned="false"
         filters="stat"/>
 web.xml配置
 <!--MySQL資料庫JNDI資料 -->
   <resource-ref>
       <description>MySQL DB Connection</description>
       <res-ref-name>jdbc/MysqlDataSource</res-ref-name>
       <res-type>javax.sql.DataSource</res-type>
       <res-auth>Container</res-auth>
   </resource-ref>

Java程式碼中獲取JNDI資料來源

  //1、初始化名稱查詢上下文

  Context ctx =new InitialContext();

  //2、通過JNDI名稱找到DataSource

 DruidDataSource ds = (DruidDataSource)ctx.lookup("java:comp/env/jdbc/MysqlDataSource");

  //3、通過ds獲取資料庫連線物件

  Connectionconn = ds.getConnection();

3.2 BoneCP連線池

Maven依賴

<dependency>
  <groupId>com.jolbox</groupId>
  <artifactId>bonecp-spring</artifactId>
  <version>0.8.0.RELEASE</version>
</dependency>

Spring整合BoneCP配置方式

<!-- Spring BoneCP 資料來源配置-->
	<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
		<!-- 資料庫驅動 -->
		<property name="driverClass" value="${jdbc.driver}" />
		<!-- 相應驅動的jdbcUrl -->
		<property name="jdbcUrl" value="${jdbc.url}" />
		<!-- 資料庫的使用者名稱 -->
		<property name="username" value="${jdbc.username}" />
		<!-- 資料庫的密碼 -->
		<property name="password" value="${jdbc.password}" />
		<!-- 檢查資料庫連線池中空閒連線的間隔時間,單位是分,預設值:240,如果要取消則設定為0 -->
		<property name="idleConnectionTestPeriod" value="60" />
		<!-- 連線池中未使用的連結最大存活時間,單位是分,預設值:60,如果要永遠存活設定為0 -->
		<property name="idleMaxAge" value="30" />
		<!-- 每個分割槽最大的連線數 -->
		<property name="maxConnectionsPerPartition" value="150" />
		<!-- 每個分割槽最小的連線數 -->
		<property name="minConnectionsPerPartition" value="5" />
	</bean>

Tomcat中BoneCP使用JNDI配置方式

<Resource
  name="JNDIName"
  auth="Container"
  type="com.jolbox.bonecp.BoneCPDataSource"
  factory="org.apache.naming.factory.BeanFactory"
  driverClass="oracle.jdbc.driver.OracleDriver"
  username="root" 
    password="root" 
  jdbcUrl="jdbc:mysql://localhost:3306/test"
  idleConnectionTestPeriod="0"
  idleMaxAge="10"
  partitionCount="1"
  maxConnectionsPerPartition="5"
  minConnectionsPerPartition="1"
  connectionTestStatement=""
  initSQL="select 1 from dual"/>

Java程式碼中獲取JNDI資料來源

  //1、初始化名稱查詢上下文

  Context ctx =new InitialContext();

  //2、通過JNDI名稱找到DataSource

  DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/MysqlDataSource");

  //3、通過ds獲取資料庫連線物件

  Connectionconn = ds.getConnection();

3.3 Tomcat Jdbc Pool連線池

Maven依賴

<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-jdbc</artifactId>
  <version>7.0.75</version>
</dependency>
<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-juli</artifactId>
  <version>7.0.75</version>
</dependency>

Spring整合Tomcat Jbdc Pool配置方式

<!--tomcat jdbc pool資料來源配置--> 
	<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
		<property name="poolProperties">
		<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
		<!--driverClassName url username password-->
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"/>
		<property name="username" value="root"/>
		<property name="password" value="root"/>
		<!--jmx support-->
		<property name="jmxEnabled" value="true"/>
		<property name="testWhileIdle" value="true"/>
		<property name="testOnBorrow" value="true"/>
		<property name="testOnReturn" value="false"/>
		<property name="validationInterval" value="30000"/>
		<property name="validationQuery" value="SELECT 1"/>
		<property name="timeBetweenEvictionRunsMillis" value="30000"/>
		<!--最大連線-->
		<property name="maxActive" value="50"/>
		<!--初始化連線-->
		<property name="initialSize" value="5"/>
		<!--最長等待時間ms-->
		<property name="maxWait" value="10000"/>
		<property name="minEvictableIdleTimeMillis" value="30000"/>
		<property name="minIdle" value="10"/>
		<!--是否允許日誌-->
		<property name="logAbandoned" value="false"/>
		<property name="removeAbandoned" value="true"/>
		<property name="removeAbandonedTimeout" value="60"/>
		<property name="jdbcInterceptors" value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"/>
	</bean>

Tomcat中context.xml檔案JNDI配置方式

<Resource   
    name="jdbc/test"  
    auth="Container"  
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"  
    testWhileIdle="true"  
    testOnBorrow="true"  
    testOnReturn="false"  
    validationQuery="SELECT 1"  
    validationInterval="30000"  
    timeBetweenEvictionRunsMillis="30000"  
    driverClassName="com.mysql.jdbc.Driver"  
    maxActive="100"  
    maxIdle="40"
    maxWait="12000"  
    initialSize="10"  
    removeAbandonedTimeout="60"  
    removeAbandoned="true"  
    logAbandoned="true"  
    minEvictableIdleTimeMillis="30000"  
    jmxEnabled="true"  
    jdbcInterceptors=  
     "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"  
    username="root" 
    password="root" 
    type="javax.sql.DataSource"   
    url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>  	

Java程式碼中獲取JNDI資料來源

//1、初始化名稱查詢上下文

  Context ctx =new InitialContext();

  //2、通過JNDI名稱找到DataSource

  DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/test");

  //3、通過ds獲取資料庫連線物件

  Connectionconn = ds.getConnection();

3.4 Apache DBCP連線池

Maven依賴

<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.4</version>
</dependency>
<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>3.2.2</version>
</dependency>
<dependency>
  <groupId>commons-pool</groupId>
  <artifactId>commons-pool</artifactId>
  <version>1.6</version>
</dependency>

Spring整合DBCP配置方式

 <!-- 配置dbcp資料來源 -->
      <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <!-- 池啟動時建立的連線數量 -->
        <property name="initialSize" value="5"/>
        <!-- 同一時間可以從池分配的最多連線數量。設定為0時表示無限制。 -->
        <property name="maxActive" value="50"/>
        <!-- 池裡不會被釋放的最多空閒連線數量。設定為0時表示無限制。 -->
        <property name="maxIdle" value="10"/>
        <!-- 在不新建連線的條件下,池中保持空閒的最少連線數。 -->
        <property name="minIdle" value="3"/>
        <!-- 設定自動回收超時連線 -->  
        <property name="removeAbandoned" value="true" />
        <!-- 自動回收超時時間(以秒數為單位) -->  
        <property name="removeAbandonedTimeout" value="200"/>
        <!-- 設定在自動回收超時連線的時候列印連線的超時錯誤  --> 
        <property name="logAbandoned" value="true"/>
        <!-- 等待超時以毫秒為單位,在丟擲異常之前,池等待連線被回收的最長時間(當沒有可用連線時)。設定為-1表示無限等待。-->  
        <property name="maxWait" value="100"/>  
      </bean>
	  

Tomcat中context.xml檔案JNDI配置方式

<Resource name="/jdbc/test" 
		type="javax.sql.DataSource" 
		driverClassName="com.sybase.jdbc3.jdbc.SybDataSource" 
		url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8" 
		username="root" 
		password="root" 
		initialSize="5"
		maxActive="50" 
		maxIdle="10" 
		minIdle="3"
		maxWait="50000" />	

Java程式碼中獲取JNDI資料來源

//1、初始化名稱查詢上下文

  Context ctx =new InitialContext();

  //2、通過JNDI名稱找到DataSource

  DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/test");

  //3、通過ds獲取資料庫連線物件

  Connectionconn = ds.getConnection();

3.5 C3p0連線池

Maven依賴

<dependency>
  <groupId>c3p0</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.1.2</version>
</dependency>

Spring整合配置方式

<!-- Spring配置c3p0資料來源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!--連線池中保留的最大連線數。Default: 15 -->
        <property name="maxPoolSize" value="100" />
        <!--連線池中保留的最小連線數。-->
        <property name="minPoolSize" value="1" />
        <!--初始化時獲取的連線數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
        <property name="initialPoolSize" value="10" />
        <!--最大空閒時間,60秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0 -->
        <property name="maxIdleTime" value="30" />
        <!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3 -->
        <property name="acquireIncrement" value="5" />
        <!--JDBC的標準引數,用以控制資料來源內載入的PreparedStatements數量。Default: 0-->
        <property name="maxStatements" value="0" />
        <!--每60秒檢查所有連線池中的空閒連線。Default: 0 -->
        <property name="idleConnectionTestPeriod" value="60" />
        <!--定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30 -->
        <property name="acquireRetryAttempts" value="30" />
        <!--獲取連線失敗將會引起所有等待連線池來獲取連線的執行緒丟擲異常。Default: false-->
        <property name="breakAfterAcquireFailure" value="true" />
        <!--因效能消耗大請只在需要的時候使用它。Default: false -->
        <property name="testConnectionOnCheckout"  value="false" />        
    </bean>

Tomcat中context.xml檔案JNDI配置方式

 <Resource 
          name="jdbc/MysqlDataSource" 
          auth="Container"
          factory="org.apache.naming.factory.BeanFactory" 
          type="com.mchange.v2.c3p0.ComboPooledDataSource"
          driverClass="com.mysql.jdbc.Driver"
          idleConnectionTestPeriod="60"
         maxPoolSize="50" 
         minPoolSize="2"
         acquireIncrement="2" 
         user="root" 
         password="root"
         jdbcUrl="jdbc:mysql://localhost:3306/test"/>	

Java程式碼中獲取JNDI資料來源

  //1、初始化名稱查詢上下文

  Context ctx =new InitialContext();

  //2、通過JNDI名稱找到DataSource

  DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/MysqlDataSource");

  //3、通過ds獲取資料庫連線物件

  Connectionconn = ds.getConnection();

4.總結

     本文所比較的5種資料庫連線池在效能方面,根據個人測試結果和參考網上資料Druid > TomcatJDBC > DBCP > C3P0,BoneCP的效能方面沒有深入比較,應該和Tomcat Jdbc差不多。對於小型的系統,併發壓力不大時,選擇哪一種資料庫連線池差別不會很大,主要考慮的應該是連線池的穩定性。當併發量較高時,一般不會選擇使用DBCP和C3P0,選擇Druid是較好的。本文給出了5種資料庫連線池通過Spring配置和Tomcat JNDI方式配置兩種方式,Spring配置一般使用單獨的屬性檔案,每一個連線池都提供了使用程式碼建立的方式,使用方式也比較類似,感興趣可以自行研究。另外連線不同的資料庫時,在配置方面的差異主要在driverClass和jdbcUrl兩項,優化配置項可以另行考慮。

歡迎大家關注博主訂閱號“Java技術日誌”,提供Java相關技術分享,從Java程式設計基礎到Java高階技術,從JavaWeb技術基礎Jsp、Servlet、JDBC到SSH、SSM開發框架,從REST風格介面設計到分散式專案實戰。剖析主流開源技術框架,用親身實踐來譜寫深度Java技術日誌。


Java技術日誌