DBCP連接池介紹

-----------------------------

目前 DBCP 有兩個(gè)版本分別是 1.3 和 1.4。

DBCP 1.3 版本需要運(yùn)行于 JDK 1.4-1.5 ,支持 JDBC 3。

DBCP 1.4 版本需要運(yùn)行于 JDK 1.6 ,支持 JDBC 4。

1.3和1.4基于同一套源代碼,含有所有的bug修復(fù)和新特性。因此在選擇DBCP版本的時(shí)候,要看你用的是什么JDK版本。

DBCP1.2版本性能一般,比c3p0差挺多。DBCP1.4和1.3,配合(依賴)commons pool 1.6的jar包,各方面功能、性能推進(jìn)到新的高峰。相對(duì)1.2版本提高不少。超越(或相當(dāng))了c3p0.建議使用DBCP1.4或1.3 +  commons pool 1.6

 

Tomcat7 中保留DBCP連接池,以兼容已有應(yīng)用。并提供了新的Tomcat JDBC pool作為DBCP的可選替代。新出的Tomcat JDBC pool,據(jù)說比DBCP 1.4要好,未接觸,也不在本文討論范圍內(nèi)。

 

DBCP連接池配置參數(shù)講解

-----------------------------

一、Apache官方DBCP文檔給出的配置示例:

可參見:http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html

<Context>

  <Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"

               maxActive="100" maxIdle="30" maxWait="10000"

               username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"

               url="jdbc:mysql://localhost:3306/javatest"/>

</Context>

 

tomcat JDBC連接池配置示例,自動(dòng)檢查連接的可用性,dbcp定時(shí)檢測連接,dbcp自動(dòng)重連的配置

Xml代碼  收藏代碼
  1. <Resource  
  2. name="jdbc/TestDB"  JNDI數(shù)據(jù)源的name,查找時(shí)用:java:comp/env/jdbc/TestDB  
  3. type="javax.sql.DataSource"  
  4. factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"  
  5. driverClassName="com.mysql.jdbc.Driver" JDBC驅(qū)動(dòng)類  
  6. url="jdbc:mysql://localhost:3306/test?  
  7. characterEncoding=UTF-8&amp;autoReconnectForPools=true&amp;rewriteBatchedStatements=true&amp;useCursorFetch=true&amp;defaultFetchSize=20" 數(shù)據(jù)庫URL地址    
  8. username="xxx" 訪問數(shù)據(jù)庫用戶名  
  9. password="xxx" 訪問數(shù)據(jù)庫的密碼  
  10.    
  11. maxWait="3000" 從池中取連接的最大等待時(shí)間,單位ms.  
  12. initialSize="10"  初始化連接  
  13. maxIdle="60"   最大空閑連接  
  14. minIdle="10"   最小空閑連接  
  15. maxActive="80" 最大活動(dòng)連接  
  16.    
  17. validationQuery = "SELECT 1"  驗(yàn)證使用的SQL語句  
  18. testWhileIdle = "true"      指明連接是否被空閑連接回收器(如果有)進(jìn)行檢驗(yàn).如果檢測失敗,則連接將被從池中去除.  
  19. testOnBorrow = "false"   借出連接時(shí)不要測試,否則很影響性能  
  20. timeBetweenEvictionRunsMillis = "30000"  每30秒運(yùn)行一次空閑連接回收器  
  21. minEvictableIdleTimeMillis = "1800000"  池中的連接空閑30分鐘后被回收  
  22. numTestsPerEvictionRun="10" 在每次空閑連接回收器線程(如果有)運(yùn)行時(shí)檢查的連接數(shù)量  
  23.       
  24. removeAbandoned="true"  連接泄漏回收參數(shù),當(dāng)可用連接數(shù)少于3個(gè)時(shí)才執(zhí)行  
  25. removeAbandonedTimeout="180"  連接泄漏回收參數(shù),180秒,泄露的連接可以被刪除的超時(shí)值  
  26. />  

 

 

DBCP連接池的自我檢測

-----------------------------

默認(rèn)配置的DBCP連接池,是不對(duì)池中的連接做測試的,有時(shí)連接已斷開了,但DBCP連接池不知道,還以為連接是好的呢。

應(yīng)用從池中取出這樣的連接訪問數(shù)據(jù)庫一定會(huì)報(bào)錯(cuò)。這也是好多人不喜歡DBCP的原因。

 

問題例一:

MySQL8小時(shí)問題,Mysql服務(wù)器默認(rèn)連接的“wait_timeout”是8小時(shí),也就是說一個(gè)connection空閑超過8個(gè)小時(shí),Mysql將自動(dòng)斷開該 connection。

但是DBCP連接池并不知道連接已經(jīng)斷開了,如果程序正巧使用到這個(gè)已經(jīng)斷開的連接,程序就會(huì)報(bào)錯(cuò)誤。

 

問題例二:

    以前還使用Sybase數(shù)據(jù)庫,由于某種原因,數(shù)據(jù)庫死了后重啟、或斷網(wǎng)后恢復(fù)。

    等了約10分鐘后,DBCP連接池中的連接還都是不能使用的(斷開的),訪問數(shù)據(jù)應(yīng)用一直報(bào)錯(cuò),最后只能重啟Tomcat問題才解決 。

 

解決方案:

    方案1、定時(shí)對(duì)連接做測試,測試失敗就關(guān)閉連接。

    方案2、控制連接的空閑時(shí)間達(dá)到N分鐘,就關(guān)閉連接,(然后可再新建連接)。

    以上兩個(gè)方案使用任意一個(gè)就可以解決以述兩類問題。如果只使用方案2,建議 N <= 5分鐘。連接斷開后最多5分鐘后可恢復(fù)。

    也可混合使用兩個(gè)方案,建議 N = 30分鐘。

    

    下面就是DBCP連接池,同時(shí)使用了以上兩個(gè)方案的配置配置

    validationQuery = "SELECT 1"  驗(yàn)證連接是否可用,使用的SQL語句

    testWhileIdle = "true"      指明連接是否被空閑連接回收器(如果有)進(jìn)行檢驗(yàn).如果檢測失敗,則連接將被從池中去除.

    testOnBorrow = "false"   借出連接時(shí)不要測試,否則很影響性能

    timeBetweenEvictionRunsMillis = "30000"  每30秒運(yùn)行一次空閑連接回收器

    minEvictableIdleTimeMillis = "1800000"  池中的連接空閑30分鐘后被回收,默認(rèn)值就是30分鐘。

    numTestsPerEvictionRun="3" 在每次空閑連接回收器線程(如果有)運(yùn)行時(shí)檢查的連接數(shù)量,默認(rèn)值就是3.

    

    解釋:

    配置timeBetweenEvictionRunsMillis = "30000"后,每30秒運(yùn)行一次空閑連接回收器(獨(dú)立線程)。并每次檢查3個(gè)連接,如果連接空閑時(shí)間超過30分鐘就銷毀。銷毀連接后,連接數(shù)量就少了,如果小于minIdle數(shù)量,就新建連接,維護(hù)數(shù)量不少于minIdle,過行了新老更替。

    testWhileIdle = "true" 表示每30秒,取出3條連接,使用validationQuery = "SELECT 1" 中的SQL進(jìn)行測試 ,測試不成功就銷毀連接。銷毀連接后,連接數(shù)量就少了,如果小于minIdle數(shù)量,就新建連接。

    testOnBorrow = "false" 一定要配置,因?yàn)樗哪J(rèn)值是true。false表示每次從連接池中取出連接時(shí),不需要執(zhí)行validationQuery = "SELECT 1" 中的SQL進(jìn)行測試。若配置為true,對(duì)性能有非常大的影響,性能會(huì)下降7-10倍。所在一定要配置為false.

    每30秒,取出numTestsPerEvictionRun條連接(本例是3,也是默認(rèn)值),發(fā)出"SELECT 1" SQL語句進(jìn)行測試 ,測試過的連接不算是“被使用”了,還算是空閑的。連接空閑30分鐘后會(huì)被銷毀。

    

 

DBCP連接池配置參數(shù)注意事項(xiàng)  

-----------------------------

maxIdle值與maxActive值應(yīng)配置的接近。

因?yàn)椋?dāng)連接數(shù)超過maxIdle值后,剛剛使用完的連接(剛剛空閑下來)會(huì)立即被銷毀。而不是我想要的空閑M秒后再銷毀起一個(gè)緩沖作用。這一點(diǎn)DBCP做的可能與你想像的不一樣。

若maxIdle與maxActive相差較大,在高負(fù)載的系統(tǒng)中會(huì)導(dǎo)致頻繁的創(chuàng)建、銷毀連接,連接數(shù)在maxIdle與maxActive間快速頻繁波動(dòng),這不是我想要的。

高負(fù)載系統(tǒng)的maxIdle值可以設(shè)置為與maxActive相同或設(shè)置為-1(-1表示不限制),讓連接數(shù)量在minIdle與maxIdle間緩沖慢速波動(dòng)。

 

timeBetweenEvictionRunsMillis建議設(shè)置值

initialSize="5",會(huì)在tomcat一啟動(dòng)時(shí),創(chuàng)建5條連接,效果很理想。

但同時(shí)我們還配置了minIdle="10",也就是說,最少要保持10條連接,那現(xiàn)在只有5條連接,哪什么時(shí)候再創(chuàng)建少的5條連接呢?

1、等業(yè)務(wù)壓力上來了, DBCP就會(huì)創(chuàng)建新的連接。

2、配置timeBetweenEvictionRunsMillis=“時(shí)間”,DBCP會(huì)啟用獨(dú)立的工作線程定時(shí)檢查,補(bǔ)上少的5條連接。銷毀多余的連接也是同理。

 

連接銷毀的邏輯

------------------------------

DBCP的連接數(shù)會(huì)在  0 - minIdle - maxIdle - maxActive  之間變化。變化的邏輯描述如下:

 

默認(rèn)未配置initialSize(默認(rèn)值是0)和timeBetweenEvictionRunsMillis參數(shù)時(shí),剛啟動(dòng)tomcat時(shí),連接數(shù)是0。當(dāng)應(yīng)用有一個(gè)并發(fā)訪問數(shù)據(jù)庫時(shí)DBCP創(chuàng)建一個(gè)連接。

目前連接數(shù)量還未達(dá)到minIdle,但DBCP也不自動(dòng)創(chuàng)建新連接已使數(shù)量達(dá)到minIdle數(shù)量(沒有一個(gè)獨(dú)立的工作線程來檢查和創(chuàng)建)。

隨著應(yīng)用并發(fā)訪問數(shù)據(jù)庫的增多,連接數(shù)也增多,但都與minIdle值無關(guān),很快minIdle被超越,minIdle值一點(diǎn)用都沒有。

直到連接的數(shù)量達(dá)到maxIdle值,這時(shí)的連接都是只增不減的。 再繼續(xù)發(fā)展,連接數(shù)再增多并超過maxIdle時(shí),使用完的連接(剛剛空閑下來的)會(huì)立即關(guān)閉,總體連接的數(shù)量穩(wěn)定在maxIdle但不會(huì)超過maxIdle。

但活動(dòng)連接(在使用中的連接)可能數(shù)量上瞬間超過maxIdle,但永遠(yuǎn)不會(huì)超過maxActive。

這時(shí)如果應(yīng)用業(yè)務(wù)壓力小了,訪問數(shù)據(jù)庫的并發(fā)少了,連接數(shù)也不會(huì)減少(沒有一個(gè)獨(dú)立的線程來檢查和銷毀),將保持在maxIdle的數(shù)量。

 

默認(rèn)未配置initialSize(默認(rèn)值是0),但配置了timeBetweenEvictionRunsMillis=“30000”(30秒)參數(shù)時(shí),剛啟動(dòng)tomcat時(shí),連接數(shù)是0。馬上應(yīng)用有一個(gè)并發(fā)訪問數(shù)據(jù)庫時(shí)DBCP創(chuàng)建一個(gè)連接。

目前連接數(shù)量還未達(dá)到minIdle,每30秒DBCP的工作線程檢查連接數(shù)是否少于minIdle數(shù)量,若少于就創(chuàng)建新連接直到達(dá)到minIdle數(shù)量。

隨著應(yīng)用并發(fā)訪問數(shù)據(jù)庫的增多,連接數(shù)也增多,直到達(dá)到maxIdle值。這期間每30秒DBCP的工作線程檢查連接是否空閑了30分鐘,若是就銷毀。但此時(shí)是業(yè)務(wù)的高峰期,是不會(huì)有長達(dá)30分鐘的空閑連接的,工作線程查了也是白查,但它在工作。到這里連接數(shù)量一直是呈現(xiàn)增長的趨勢(shì)。

當(dāng)連接數(shù)再增多超過maxIdle時(shí),使用完的連接(剛剛空閑下來)會(huì)立即關(guān)閉,總體連接的數(shù)量穩(wěn)定在maxIdle。停止了增長的趨勢(shì)。但活動(dòng)連接(在使用中的連接)可能數(shù)量上瞬間超過maxIdle,但永遠(yuǎn)不會(huì)超過maxActive。

這時(shí)如果應(yīng)用業(yè)務(wù)壓力小了,訪問數(shù)據(jù)庫的并發(fā)少了,每30秒DBCP的工作線程檢查連接(默認(rèn)每次查3條)是否空閑達(dá)到30分鐘(這是默認(rèn)值),若連接空閑達(dá)到30分鐘,就銷毀連接。這時(shí)連接數(shù)減少了,呈下降趨勢(shì),將從maxIdle走向minIdle。當(dāng)小于minIdle值時(shí),則DBCP創(chuàng)建新連接已使數(shù)量穩(wěn)定在minIdle,并進(jìn)行著新老更替。

 

配置initialSize=“10”時(shí),tomcat一啟動(dòng)就創(chuàng)建10條連接。其它同上。

 

minIdle要與timeBetweenEvictionRunsMillis配合使用才有用,單獨(dú)使用minIdle不會(huì)起作用。

 

 

Tomcat中配置DBCP連接池

-----------------------------

Tomcat自帶DBCP的包,是$CATALINA_HOME/lib/tomcat-dbcp.jar。

tomcat-dbcp.jar含有commons pool、commons DBCP兩個(gè)包的內(nèi)容。但只含有與連接池有關(guān)的類。

數(shù)據(jù)源配置在context.xml文件中, 要在tomcat的lib目錄中放jdbc 驅(qū)動(dòng)包

數(shù)據(jù)源配置在server.xml的host中,不需要在tomcat的lib目錄中放jdbc 驅(qū)動(dòng)包,只使用工程中的jdbc驅(qū)動(dòng)包

 

 

JNDI配置:更改tomcat的server.xml或context.xml

 

    全局的數(shù)據(jù)源:

    如果需要配置全局的 Resource,則在server.xml的GlobalNamingResources節(jié)點(diǎn)里加入Resource,再在Context節(jié)點(diǎn)里加入ResourceLink的配置。

    全局的resource只是為了重用,方便所有該tomcat下的web工程的數(shù)據(jù)源管理,但如果你的tomcat不會(huì)同時(shí)加載多個(gè)web工程,也就是說一個(gè)tomcat只加載一個(gè)web工程時(shí),是沒有必要配置全局的resource的。

 

每個(gè)web工程一個(gè)數(shù)據(jù)源:

在$CATALINA_HOME/conf/context.xml的根節(jié)點(diǎn)Context里加入Resource配置。這種配置方法,你在context.xml配置了一個(gè)數(shù)據(jù)源,但Tomcat中有同時(shí)運(yùn)行著5個(gè)工程,那了就壞事兒了,這個(gè)在Tomcat啟動(dòng)時(shí)數(shù)據(jù)源被創(chuàng)建了5份,每個(gè)工程1份數(shù)據(jù)源。連接數(shù)會(huì)是你配置的參數(shù)的5倍。

只有在你的Tomcat只加載一個(gè)web工程時(shí),才可以直接以context.xml配置數(shù)據(jù)源。

 

<Resource name="jdbc/testDB"       //指定的jndi名稱,會(huì)用于spring數(shù)據(jù)源bean的配置和ResourceLink的配置

               type="javax.sql.DataSource"   //數(shù)據(jù)源類型,使用標(biāo)準(zhǔn)的javax.sql.DataSource

               driverClassName="com.mysql.jdbc.Driver"    //JDBC驅(qū)動(dòng)器 

               url="jdbc:mysql://localhost:3306/test" //數(shù)據(jù)庫URL地址             

               username="test"     //數(shù)據(jù)庫用戶名

               password="test"   //數(shù)據(jù)庫密碼

               maxIdle="40"   //最大的空閑連接數(shù)

               maxWait="4000" //當(dāng)池的數(shù)據(jù)庫連接已經(jīng)被占用的時(shí)候,最大等待時(shí)間

               maxActive="40" //連接池當(dāng)中最大的數(shù)據(jù)庫連接

               removeAbandoned="true" 

               removeAbandonedTimeout="180"

               logAbandoned="true" //被丟棄的數(shù)據(jù)庫連接是否做記錄,以便跟蹤

               factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" />

 

      這里的factory指的是該Resource 配置使用的是哪個(gè)數(shù)據(jù)源配置類,這里使用的是tomcat自帶的標(biāo)準(zhǔn)數(shù)據(jù)源Resource配置類,這個(gè)類也可以自己寫,實(shí)現(xiàn)javax.naming.spi.ObjectFactory 接口即可。某些地方使用的commons-dbcp.jar中的org.apache.commons.dbcp.BasicDataSourceFactory,如果使用這個(gè)就需把commons-dbcp.jar及其依賴的jar包,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不夠的。

 

     ResourceLink 的配置有多種:

 

     1)tomcat安裝目錄下的conf/context.xml,把全局的resource直接公開給該tomcat下的所有web工程,在Context節(jié)點(diǎn)中加入:

<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>   

不建議在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置數(shù)據(jù)源,原因上面已說明了。   

 

     2)tomcat安裝目錄下的conf/server.xml,該方法可以指定把哪些source綁定到哪個(gè)web工程下。

<!-- 新增,第一行為加載的工程配置,第二行是該工程需要的ResourceLink配置 -->

<context docBase="/web/webapps/phoenix" path="" reloadable="false"> 

      <ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>

</context>

也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置數(shù)據(jù)源。

 

     3)安裝目錄下的conf/localhost/下建立一個(gè)xml文件,文件名是<yourAppName>.xml。比如工程名為test,則該xml名為test.xml。

<?xml version="1.0" encoding="UTF-8"?>

<Context>   

    <ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>       

</context>

也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置數(shù)據(jù)源。

 

     4)tomcat安裝目錄下的\webapps\test\META-INF\context.xml的Context節(jié)點(diǎn)中增加:

<ResourceLink global="jdbc/testMDB" name="jdbc/testMDB" type="javax.sql.DataSource"/>

也可在此文件中,不使用<ResourceLink/>,而使用<Resource/>直接配置數(shù)據(jù)源。

 

 

本文內(nèi)容都在tomcat6.0上運(yùn)行測試過,還下載了commons DBCP的源碼,加入了跟蹤日志,用于驗(yàn)證本文的理論。

 

連接池排名(純個(gè)人看法)

-----------------------------

Tomcat JDBC pool

DBCP 1.4

c3p0   速度不錯(cuò)

BoneCP 速度不錯(cuò),但會(huì)啟用很多附加線程做回收、關(guān)閉工作。

Proxool 在高并發(fā)時(shí)出現(xiàn)異常

DBCP 1.2

DBPool 最差,墊底。

參考文檔《Java連接池評(píng)估報(bào)告》