DBCP老矣,但能飯

數(shù)據(jù)庫連接池在J2EE領域是一個不可缺失的組件,盡管DRUID越來越流行,但是DBCP作為一個老牌的數(shù)據(jù)庫連接池一直在企業(yè)系統(tǒng)中默默的奉獻著自己。比如我們的大部分系統(tǒng)還都是使用DBCP,那么我們很有必要對DBCP有一個熟悉的認識。鑒于這樣的需求,我根據(jù)相關資料(文后都有列出)結合自己的認知重新做了一個梳理,一來自己可以在以后的工作中回過頭來溫習,二來也希望能夠幫助其他同學對DBCP以及涉及到的相關概念和知識比如超時機制,連接池原理等有一個復習。我們都知道操作一個數(shù)據(jù)庫的流程,創(chuàng)建數(shù)據(jù)源,獲得鏈接,構造statement,執(zhí)行請求,接下來我們逐步梳理總結。

一、相關概念復習

在談DBCP之前我們先來復習一下幾個相關的概念:

1.1、JNDI

Java Naming and Directory Interface (JNDI)JNDI API被用于執(zhí)行名字和目錄服務。它提供了一致的模型來存取和操作企業(yè)級的資源。JNDI的api位于javax.naming包中,它的作用是,它可以把對象放到一個容器中(JNDI容器),JAVA對象在這個容器中都有一個名稱,程序則可以通過這個名稱來獲取對象,例如下面這樣:

// Construct BasicDataSource
  BasicDataSource bds = new BasicDataSource();
  bds.setDriverClassName("org.apache.commons.dbcp2.TesterDriver");
  bds.setUrl("jdbc:apache:commons:testdriver");
  bds.setUsername("username");
  bds.setPassword("password");

  ic.rebind("jdbc/basic", bds);
   
  // Use
  InitialContext ic2 = new InitialContext();
  DataSource ds = (DataSource) ic2.lookup("jdbc/basic");
  assertNotNull(ds);
  Connection conn = ds.getConnection();
  assertNotNull(conn);
  conn.close();
1.2、JDBC
JDBC Type 4.png

JAVA數(shù)據(jù)庫連接(Java Database Connectivity,簡稱JDBC),是java語言中用來規(guī)范話我們的客戶端應用程序,比如我們的web應用程序等如何訪問關系型數(shù)據(jù)庫的應用程序接口,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的方法。
JDBC驅動程序一共有四種類型:

  • 類型1-JDBC-ODBC橋
  • 類型2-本地API驅動
  • 類型3-網(wǎng)絡協(xié)議驅動
  • 類型4-本地協(xié)議驅動
    我們常用的是類型4-本地協(xié)議驅動,這種類型的驅動使用socket鏈接,直接在客戶端和數(shù)據(jù)庫之間進行通信。
    優(yōu)點是1-訪問速度快 2-最直接,最純粹的JAVA實現(xiàn)
    缺點是1-需要每個數(shù)據(jù)庫廠商提供自己的JDBC驅動。2-需要針對不同的數(shù)據(jù)庫使用不同的驅動程序。
    JDBC的API在jdk的java.sql包中,擴展的內(nèi)容在javax.sql包中。主要包括(斜體代表接口,需驅動程序提供者來具體實現(xiàn)):
  • DriverManager:負責加載各種不同驅動程序(Driver),并根據(jù)不同的請求,向調(diào)用者返回相應的數(shù)據(jù)庫連接(Connection)。
  • Driver:驅動程序,會將自身加載到DriverManager中去,并處理相應的請求并返回相應的數(shù)據(jù)庫連接(Connection)。
  • Connection:數(shù)據(jù)庫連接,負責進行與數(shù)據(jù)庫間的通訊,SQL執(zhí)行以及事務處理都是在某個特定Connection環(huán)境中進行的。可以產(chǎn)生用以執(zhí)行SQL的Statement。
  • Statement:用以執(zhí)行SQL查詢和更新(針對靜態(tài)SQL語句和單次執(zhí)行)。
  • PreparedStatement:用以執(zhí)行包含動態(tài)參數(shù)的SQL查詢和更新(在服務器端編譯,允許重復執(zhí)行以提高效率)。
  • CallableStatement:用以調(diào)用數(shù)據(jù)庫中的存儲過程。
  • SQLException:代表在數(shù)據(jù)庫連接的創(chuàng)建和關閉和SQL語句的執(zhí)行過程中發(fā)生了例外情況(即錯誤)。
1.3、DBCP數(shù)據(jù)庫連接池和JDBC之間的關系

數(shù)據(jù)庫連接池負責創(chuàng)建(通過JDBC API)、管理、銷毀數(shù)據(jù)庫的連接。應用程序可以從數(shù)據(jù)庫連接池中重復使用一個現(xiàn)有的連接,而不是重新創(chuàng)建一個。連接池,common-pool中的GenericObjectPool它負責緩存和管理連接;連接,這是是指PoolableConnection;連接池和連接一對多的關系。池化技術是通過commons-pool來實現(xiàn)的,每個連接是一個對象,換言之,是對象池的使用與管理。DBCP連接池是基于commons-pool這種對象池來實現(xiàn)的。

二、commons-pool的理解

Apache commons-pool是一種對象池技術,我們使用的很多涉及池的場景一般都是基于該組件來實現(xiàn)的,DBCP數(shù)據(jù)庫連接池也是基于commons-pool來實現(xiàn)的,因此我們先來了解下這種對象池技術。下面這個圖是對象池的對象生命周期流程圖。

對象池.png

三、DBCP核心類圖及序列圖

3.1、BasicDataSource.java
BasicDataSource.gif
3.2、ConnectionFactory.java
ConnectionFactory.gif
3.3、PoolingDataSource.java
PoolingDataSource.gif
3.4、PoolingConnection.java
PoolingConnection.gif
3.5、Delegating.java
Delegating.gif
3.6、AbandonedObjectPool.java
AbandonedObjectPool.gif
3.7、創(chuàng)建數(shù)據(jù)源createDataSource
createDataSource序列圖.gif
3.8、創(chuàng)建連接getConnection
getConnection序列圖.gif
3.9、創(chuàng)建statement prepareStatement
prepareStatement序列圖.gif

四、DBCP配置及使用

DBCP是Apache下的一個開源數(shù)據(jù)庫連接池,我們在使用的時候需要兩個JAR文件,分別是commons-dbcp.jar(連接池的實現(xiàn))和commons-pool.jar(連接池實現(xiàn)的依賴庫),不過我們在使用的時候只需要引入下面mvaen坐標,commons-pool是在commons-dbcp里面隱含引用了。注意一點,就是從1.x升級到2.x的時候,由于dbcp的包路徑已經(jīng)變了,需要升級者修改局部代碼。

<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>2.2</version>
</dependency>

DBCP可以在應用程序中獨立的使用,也可以與web應用服務器整合使用。

4.1、tomcat中使用

比如tomcat的連接池就是采用該連接池來實現(xiàn)的。如下:

<Context> 
    <Resource name="jdbc/datasource" auth="Container"  type="javax.sql.DataSource" 
            username="root" password="root"  driverClassName="com.mysql.jdbc.Driver" 
            url="jdbc:mysql://192.168.0.1:3306/test"  maxActive="8" maxIdle="4"  /> 
</Context>
4.2、應用程序中獨立使用
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close"> 
        <!--如果沒有配置 destroy-method="close" ,但是重啟次數(shù)如果太頻繁的話,將造成重啟tomcat后舊的數(shù)據(jù)庫連接池的連接不釋放,連接堆滿了,后續(xù)啟動無法建立連接-->
        
        <!--數(shù)據(jù)庫連接相關配置,用戶名、密碼、連接地址、驅動-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
        <property name="url" value="jdbc:mysql://192.168.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8"/>  
        <property name="username" value="root"/>  
        <property name="password" value="root"/>

        <!--數(shù)據(jù)庫連接屬性 connectTimeout:建立連接的超時時間,socketTimeout:客戶端和服務進行數(shù)據(jù)交互的時間,是指兩者之間如果兩個數(shù)據(jù)包之間的時間大于該時間則認為超時-->
        <property name="connectionProperties"
                  value="connectTimeout=2000;socketTimeout=15000"/>
        
        <!--以下屬性也有的建議,值配置一樣-->
        <!--initialSize: 初始化連接-->
        <property name="initialSize" value="30"/>  
        <!--maxActive: 最大連接數(shù)量-->    
        <property name="maxActive" value="150"/>  
        <!--minIdle: 最小空閑連接-->    
        <property name="minIdle" value="5"/>  
        <!--maxIdle: 最大空閑連接-->    
        <property name="maxIdle" value="20"/>  
        
        
        <!--等待獲取連接池連接的時間(在1.x版本是maxWait),不要太大-->
        <property name="maxWaitMillis" value="500"/>
        <!--連接池中的連接空閑多久,從池中刪除,單位MS。小于0,則表示不啟動-->   
        <property name="minEvictableIdleTimeMillis" value="" />
        <!--檢查失效連接的定時器執(zhí)行間隔,單位MS,小于0,則表示不啟動。注意:mysql數(shù)據(jù)庫如果8小時內(nèi)沒有連接請求,則連接會自動斷開,因此這個參數(shù)如果并發(fā)量不大的情況下還是要配上-->    
        <property name="timeBetweenEvictionRunsMillis" value="" />
       
    </bean>

更多的配置請參照https://commons.apache.org/proper/commons-dbcp/configuration.html

五、連接超時機制

對網(wǎng)絡資源訪問的時候,超時設置是必須的。沒有超時的保護,一旦依賴資源發(fā)生故障或者網(wǎng)絡故障,就會引起線程堆積,甚至發(fā)生雪崩。

5.1、超時層級
超時層次.png

從這張圖中我們也能夠看出DBCP并不參與數(shù)據(jù)庫超時的處理,它只負責管理連接。根據(jù)上圖我們可以看到超時有一個依賴層級,上層超時依賴下層超時。依次為:事務超時->Statement超時->JDBC Driver socket超時->操作系統(tǒng)超時。

5.1.1、事務超時:

事務是應用層級的概念,我們知道事務是有一組SQL執(zhí)行單元組成的。那么事務超時的時間閾值,實際是Statement超時N個需要執(zhí)行的Statement數(shù)量。比如一個事務里面有3條Statement,每條Statement的執(zhí)行時間是50MS,其它業(yè)務上的邏輯執(zhí)行時間+框架執(zhí)行時間未100ms,那么最終事務的超時時間為:350+100=250ms。

5.1.2、statement超時:

用來限制sql語句的執(zhí)行時間,通過setQueryTimeout(int timeout)來設置,不過現(xiàn)在大都是ibatis了,可以通過 SqlMapConfig.xml 中的 setting 屬性defaultStatementTimeout 來設置全局的 statement 超時缺省值<settings defaultStatementTimeout="15"/>,還可以在每個sql.xml文件中,根據(jù)業(yè)務實際需要來設置<select timeout="10"/> <insert timeout="10"/> <update timeout="10"/>,這樣就會覆蓋掉全局的值,而采用具體的閾值。

5.1.3、Socket超時:

這是底層的一種超時,因為我們使用的JDBC驅動類型是TYPE4,它是基于socket來通信的。mysql的jdbc驅動中的connectTimeout 和 socketTimeout 的默認值是 0 ,這意味著不會發(fā)生超時。所以我們必須在dbcp配置中設置這兩個值。<property name="connectionProperties" value="connectTimeout=2000;socketTimeout=15000"/>
connectTimeout為建立連接的超時時間,socketTimeout為JDBC客戶端和數(shù)據(jù)庫服務器之間數(shù)據(jù)交互的時間。注意這里配置的socketTimeout的值必須要大于Statement的超時時間值。否則Statement超時就沒有意義,也不能生效。

5.1.4、操作系統(tǒng)的socket超時

linux操作系統(tǒng)也會設置socket超時,比如我們這邊的服務器一般配置的是20分鐘,因為公司的linux服務器的KeepAlive檢查周期為20分鐘。這樣即使上面的socketTimeout值為0用不超時,也還是要收到linux服務器的超時限制,也就是由于網(wǎng)絡原因引起的數(shù)據(jù)庫網(wǎng)絡連接問題也不會超過20分鐘。

5.2、Mysql處理超時的機制及原理
MySQL 的 Statement 超時執(zhí)行過程.png

上圖是一mysql在執(zhí)行一個命令的過程中的步驟以及發(fā)生超時現(xiàn)象后的處理機制
1、通過Connection的createStatement()方法去創(chuàng)建一個Statement,以便后續(xù)進行讀寫操作
2、執(zhí)行第1步創(chuàng)建的Statement的executeQuery()方法
3、將查詢請求命令發(fā)送到mysql數(shù)據(jù)庫服務器
4、創(chuàng)建一個超時線程(從5.1版本以后在,創(chuàng)建每個連接的時候,會隨之創(chuàng)建一個處理超時的線程timeout-execution)
5、把當前的statement對象注冊到超時線程timeout-execution中
6、發(fā)生了超時(閾值是你在Statement執(zhí)行前候配置的setQueryTimeout(int timeout),如果是mybatis則是在配置文件里面配置的值defaultStatementTimeout="15",單位s)
7、超時線程會重新創(chuàng)建一個新的Connection,這個Connection的屬性配置都跟先前的一樣
8、用新創(chuàng)建的這個Connection去發(fā)送取消查詢請求

六、各種數(shù)據(jù)庫連接池性能對比

測試環(huán)境,OS: OS X 10.8.2 CPU:intel i7 2GHz 4 core JVM:java version "1.7.0_05"
測試執(zhí)行申請歸還連接1,000,000(一百萬)次總耗時性能對比,Java7 的基準測試結果如下:

各種連接池性能對比.png

Druid是性能最好的數(shù)據(jù)庫連接池,DBCP性能屬于中上,我們在以后的業(yè)務場景中可以根據(jù)實際需要做選擇。這個是DRUID的官方測試,不過可以利用測試代碼自己進行驗證一遍,測試代碼如下:https://github.com/alibaba/druid/blob/master/src/test/java/com/alibaba/druid/benckmark/pool/Case1.java

七、總結

我們梳理了數(shù)據(jù)庫連接相關的概念,JNDI、JDBC、連接池,DBCP的核心類和時序圖,另外我們還梳理了commons的池化組件,最后我們描述了JDBC連接過程中的超時機制,包括超時依賴和超時的機制原理,超時的知識點對與我們非常重要,超時是一種防護措施,當依賴DB性能變慢或者網(wǎng)絡故障的情況下,可以快速失敗。以便保護我們的應用程序不會導致線程堆積而發(fā)生雪崩這類致命事故。DBCP經(jīng)歷了各種互聯(lián)網(wǎng)應用的驗證,穩(wěn)定可靠的性能會一直服務于我們的系統(tǒng)中。當今比較流行的spring boot內(nèi)部集成的Tomcat應用依然在使用dbcp連接池。

轉載請注明出處,并附上鏈接http://www.lxweimin.com/p/7e1b36c7fb82

參考資料
https://www.cubrid.org/blog/understanding-jdbc-internals-and-timeout-configuration
https://zh.wikipedia.org/wiki/Java%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5
http://shift-alt-ctrl.iteye.com/blog/1917782
https://commons.apache.org/proper/commons-pool/index.html
https://commons.apache.org/proper/commons-dbcp/index.html

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內(nèi)容