文章出處

關于數據庫連接池DBCP的關注源于剛剛結束的一輪測試,測試內容是衡量某Webserver服務創建用戶接口的性能。這是一款典型的tomcat應用,使用的測試工具是Grinder。DBCP作為tomcat服務器常用的數據庫連接池,其性能表現直接關乎應用的性能。

1.遇到的問題

       當并發量增加到100時,該接口出現瓶頸,此時TPS接近400,如下圖。但是服務端CPU和內存等資源并未達到瓶頸,服務器CPU使用率僅為30%,內存使用率為40%。監控到的javaMethod慢方法為incrAppAccountsSize(),該方法的平均響應時間達206ms。該方法的功能是增加app下的用戶數,每當我們在該app下創建一個用戶,相應app的總用戶數就會加1,緩存中響應的數據也會更新。因此該方法涉及數據庫和redis的相關操作。


數據庫的插入操作通常比較耗時,添加數據庫監控后,該接口相關sql語句執行時間如下,可見相關操作的sql語句的平均耗時在20ms左右,然而慢方法的耗時卻在200ms左右,因此性能瓶頸不在sql語句。


因此有必要分析下代碼執行過程中的堆棧信息,看看慢方法涉及哪些調用。通過jstack dump出現場的堆棧信息如下。

2.數據庫連接池DBCP

         眾所周知,數據庫的最大連接數是有限的,服務端與數據庫建立連接的過程也是非常消耗資源的,因此高效利用數據庫連接是非常必要的。java提供了一套專門與數據庫打交道的api--jdbc,基于jdbc開發人員可以實現服務端與數據庫端的通信。每次執行數據庫操作通常需要經歷建立數據庫連接、執行數據庫操作、斷開數據庫連接三步。當數據庫并發訪問量大時或請求頻繁時,頻繁的建立連接再斷開連接會給服務端帶來很大的額外開銷,嚴重制約著服務端的性能。

         因此,數據庫連接需要一套維護策略,基于不同的維護策略出現了很多開源的數據庫連接組件,DBCP(DataBase Connection Pool)就是其中一個,也據信是目前性能最好的數據庫連接組件。除了DBCP以外還有C3P0,、proxool等也是比較常見的。在一定的維護策略下,數據庫連接可以實現復用以及再回收。DBCP的使用很簡單引入commons-dbcp依賴,并配置好相關參數即可。DBCP的相關配置參數如下,

  • maxActive:最大連接數

  • maxIdle:最大空閑連接數

  • minIdle:最小空閑連接數

  • initialSize:初始連接數

  • minEvictableIdleTimeMillis:對連接進行有效性校驗的周期

        maxActive的值通常根據自己應用的峰值并發量進行設置,當并發量高于該值后超過的請求會排隊等待連接釋放;當并發量介于maxIdle和maxActive之間時,使用結束的連接會被立即斷開,新來的請求會重新創建連接;當并發量介于minIdle和maxIdle之間時,新來的請求會從連接池充獲取一個已經建立的且空閑的連接,迅速實現連接復用。因此通常情況下應用的并發量應該維持在minIdle與maxIdle之間。這樣才能保證新來的請求迅速獲取一個已經建立的連接。通常情況下initialSize會小于或等于minIdle當服務器重啟后服務器會與數據庫節點保持initialSize個連接,當并發量超多initialSize后連接數會依次上漲至minIdle、maxIdle和maxActive,當連接空閑時根據回收策略連接會被回收,并最終回落至minIdle。

3.問題分析

        結合DBCP以及第一節中利用jstack dump出的堆棧信息,可見服務端一直處于連接斷開的過程中。而DBCP建立連接的速度比斷開連接快,因此服務端一直卡在資源釋放的階段。服務端的DBCP的相關配置如下:

  • maxActive:500

  • maxIdle:50

  • minIdle:30

  • initialSize:30

       并發訪問量為160,在maxIdle和maxActive之間,因此當一個連接使用結束后由于當前連接數大于maxIdle連接無法被復用會被立即斷開,新來一個請求也無法獲取一個空閑的連接需要重新建立一個新的連接,由于斷開連接的響應時間較慢,斷開連接都在等待資源的釋放,大量的線程出現排隊,這樣就出現了通過jstack看到的,線程被block住的現象。針對這種問題有兩種優化思路:

  1. 提高連接池的復用率。

  2. 增加空閑連接的數量。

        通過提高連接復用率來解決這種問題的前提是數據庫操作的執行速度夠快,監控數據庫sql執行速度在20ms左右,如果多個數據庫操作排隊對性能影響不大,基于這種思路可以降低maxActive的值。當并發訪問量達到上限后不再分配更多的數據庫連接,而是等待前一個連接使用結束,由于maxActive與maxIdle的差值變小,因此需要斷開的連接的數量變少,這樣可以避免由于連接的斷開性能較差導致大量線程被block住的情況。將maxActive的值修改為100后,160并發創建用戶的性能如下。

慢方法incrAppAccountsSize的響應時間由800多毫秒,減小到80多毫秒,通過jstack抓取現場堆棧信息可以看到有些線程被block在getConnection狀態,線程獲取連接出現排隊,但是由于數據庫操作響應時間很快,這種線程被block在getConnection狀態的時間并不長。

        通過增加空閑連接的數量來也可以改善頻繁斷開連接所帶來的性能問題,由于當前并發量超過了最大空閑連接數,增加空閑連接數可以減少斷開連接的數量,提高復用率。但是增加空閑連接的數量會占用寶貴的數據庫連接資源,影響服務擴展。將maxIdle大小調整為200后,160并發創建用戶的性能如下。

以上數據表明,通過這兩種方式可以有效的提高慢方法的響應時間。通過增加空閑連接的數量帶來的性能提升優于增加復用率,因為增加空閑連接的數量可以避免排隊耗時。單純的增加maxIdle的值會使得空閑連接的數量增加。并發連接也不會一直維持在maxIdle這個水平,如果當前的并發壓力減小了,DBCP本身也有連接回收策略。空閑連接的回收是通過這兩個參數來實現的。

  • minEvictableIdleTimeMillis:空閑連接過期時間

  • timeBetweenEvictionRunsMillis:空閑連接回收觸發周期

        當連接池中的空閑連接空閑了minEvictableIdleTimeMillis這么長時間后,該連接會被置為可回收狀態。DBCP會按照timeBetweenEvictionRunsMillis的時間進行周期回收。最在沒有額外壓力的前提下終空閑連接穩定到minIdle水平。

4.總結

       數據庫連接池的maxIdle和maxActive之間的差值不宜不過大。如果數據庫操作響應時間很快的話可以提高連接復用率,但是這么做會出現連接排隊的情況。如果數據庫操作的響應時間較慢可以增加空閑連接的數量。由于數據庫總的連接數是一定的,單個服務的具體數據庫連接配額應該根據服務的壓力進行調整。

 

原創文章

禁止其他公眾賬號轉載


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()