1. 程式人生 > >dbcp的基本配置及相關問題總結

dbcp的基本配置及相關問題總結

 


1. 引入dbcp (選擇1.4)
Java程式碼 
<dependency>  
    <groupId>com.alibaba.external</groupId>  
    <artifactId>jakarta.commons.dbcp</artifactId>  
    <version>1.4</version>  
</dependency> 
<dependency>
 <groupId>com.alibaba.external</groupId>
 <artifactId>jakarta.commons.dbcp</artifactId>
 <version>1.4</version>
</dependency>
 

2. dbcp的基本配置

相關配置說明:

initialSize :連線池啟動時建立的初始化連線數量(預設值為0)
maxActive :連線池中可同時連線的最大的連線數(預設值為8,調整為20,高峰單機器在20併發左右,自己根據應用場景定)
maxIdle:連線池中最大的空閒的連線數,超過的空閒連線將被釋放,如果設定為負數表示不限制(預設為8個,maxIdle不能設定太小,因為假如在高負載的情況下,連線的開啟時間比關閉的時間快,會引起連線池中idle的個數上升超過maxIdle,而造成頻繁的連線銷燬和建立,類似於jvm引數中的Xmx設定)
minIdle:連線池中最小的空閒的連線數,低於這個數量會被建立新的連線(預設為0,調整為5,該引數越接近maxIdle,效能越好,因為連線的建立和銷燬,都是需要消耗資源的;但是不能太大,因為在機器很空閒的時候,也會建立低於minidle個數的連線,類似於jvm引數中的Xmn設定)
maxWait  :最大等待時間,當沒有可用連線時,連線池等待連線釋放的最大時間,超過該時間限制會丟擲異常,如果設定-1表示無限等待(預設為無限,調整為60000ms,避免因執行緒池不夠用,而導致請求被無限制掛起)
poolPreparedStatements:開啟池的prepared(預設是false,未調整,經過測試,開啟後的效能沒有關閉的好。)
maxOpenPreparedStatements:開啟池的prepared 後的同時最大連線數(預設無限制,同上,未配置)
minEvictableIdleTimeMillis  :連線池中連線,在時間段內一直空閒,被逐出連線池的時間
(預設為30分鐘,可以適當做調整,需要和後端服務端的策略配置相關)
removeAbandonedTimeout  :超過時間限制,回收沒有用(廢棄)的連線(預設為 300秒,調整為180)
removeAbandoned  :超過removeAbandonedTimeout時間後,是否進行沒用連線(廢棄)的回收(預設為false,調整為true)


removeAbandoned引數解釋:
如果開啟了removeAbandoned,當getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)時被觸發.
舉例當maxActive=20, 活動連線為18,空閒連線為1時可以觸發"removeAbandoned".但是活動連線只有在沒有被使用的時間超過"removeAbandonedTimeout"時才被回收
logAbandoned: 標記當連線被回收時是否列印程式的stack traces日誌(預設為false,未調整)


一般會是幾種情況出現需要removeAbandoned: 
程式碼未在finally釋放connection , 不過我們都用sqlmapClientTemplate,底層都有連結釋放的過程
遇到資料庫死鎖。以前遇到過後端儲存過程做了鎖表操作,導致前臺叢集中連線池全都被block住,後續的業務處理因為拿不到連結所有都處理失敗了。


一份優化過的配置:
基本配置程式碼 
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">   
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
    <property name="url" value="xxxx" />  
    <property name="username"><value>xxxx</value></property>  
        <property name="password"><value>xxxxx</value></property>  
        <property name="maxActive"><value>20</value></property>  
        <property name="initialSize"><value>1</value></property>  
        <property name="maxWait"><value>60000</value></property>  
        <property name="maxIdle"><value>20</value></property>  
        <property name="minIdle"><value>3</value></property>  
        <property name="removeAbandoned"><value>true</value></property>  
        <property name="removeAbandonedTimeout"><value>180</value></property>  
        <property name="connectionProperties"><value>clientEncoding=GBK</value></property>  
</bean> 
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 <property name="driverClassName" value="com.mysql.jdbc.Driver" />
 <property name="url" value="xxxx" />
 <property name="username"><value>xxxx</value></property>
        <property name="password"><value>xxxxx</value></property>
        <property name="maxActive"><value>20</value></property>
        <property name="initialSize"><value>1</value></property>
        <property name="maxWait"><value>60000</value></property>
        <property name="maxIdle"><value>20</value></property>
        <property name="minIdle"><value>3</value></property>
        <property name="removeAbandoned"><value>true</value></property>
        <property name="removeAbandonedTimeout"><value>180</value></property>
        <property name="connectionProperties"><value>clientEncoding=GBK</value></property>
</bean>
 


2. dbcp的連結validate配置
dbcp是採用了commons-pool做為其連線池管理,testOnBorrow,testOnReturn, testWhileIdle是pool是提供的幾種校驗機制,通過外部鉤子的方式回撥dbcp的相關資料庫連結(validationQuery)校驗
dbcp相關外部鉤子類:PoolableConnectionFactory,繼承於common-pool PoolableObjectFactory
dbcp通過GenericObjectPool這一入口,進行連線池的borrow,return處理
testOnBorrow : 顧明思義,就是在進行borrowObject進行處理時,對拿到的connection進行validateObject校驗
testOnReturn : 顧明思義,就是在進行returnObject對返回的connection進行validateObject校驗,個人覺得對資料庫連線池的管理意義不大
testWhileIdle : 關注的重點,GenericObjectPool中針對pool管理,起了一個Evict的TimerTask定時執行緒進行控制(可通過設定引數timeBetweenEvictionRunsMillis>0),定時對執行緒池中的連結進行validateObject校驗,對無效的連結進行關閉後,會呼叫ensureMinIdle,適當建立連結保證最小的minIdle連線數。
timeBetweenEvictionRunsMillis,設定的Evict執行緒的時間,單位ms,大於0才會開啟evict檢查執行緒

validateQuery, 代表檢查的sql
validateQueryTimeout,代表在執行檢查時,通過statement設定,statement.setQueryTimeout(validationQueryTimeout)
numTestsPerEvictionRun,代表每次檢查連結的數量,建議設定和maxActive一樣大,這樣每次可以有效檢查所有的連結.
Validate配置程式碼 
<property name="testWhileIdle"><value>true</value></property> <!-- 開啟檢查,用非同步執行緒evict進行檢查 -->  
    <property name="testOnBorrow"><value>false</value></property>  
    <property name="testOnReturn"><value>false</value></property>  
    <property name="validationQuery"><value>select sysdate from dual</value></property>  
    <property name="validationQueryTimeout"><value>1</value></property>  
    <property name="timeBetweenEvictionRunsMillis"><value>30000</value></property>  
    <property name="numTestsPerEvictionRun"><value>20</value></property>  
    <property name="testWhileIdle"><value>true</value></property> <!-- 開啟檢查,用非同步執行緒evict進行檢查 -->
        <property name="testOnBorrow"><value>false</value></property>
        <property name="testOnReturn"><value>false</value></property>
        <property name="validationQuery"><value>select sysdate from dual</value></property>
        <property name="validationQueryTimeout"><value>1</value></property>
        <property name="timeBetweenEvictionRunsMillis"><value>30000</value></property>
        <property name="numTestsPerEvictionRun"><value>20</value></property>
 
 相關配置需求:
 

目前網站的應用大部分的瓶頸還是在I/O這一塊,大部分的I/O還是在資料庫的這一層面上,每一個請求可能會呼叫10來次SQL查詢,如果不走事務,一個請求會重複獲取連結,如果每次獲取連結都進行validateObject,效能開銷不是很能接受,可以假定一次SQL操作消毫0.5~1ms(一般走了網路請求基本就這數)
網站異常資料庫重啟,網路異常斷開的頻率是非常低的,一般也就在資料庫升級,演習維護時才會進行,而且一般也是選在晚上,訪問量相對比較低的請求,而且一般會有人員值班關注,所以非同步的validateObject是可以接受,但一個前提需要確保能保證在一個合理的時間段內,資料庫能完成自動重聯。


從程式碼層面簡單介紹下dbcp的validate實現:


1.  common-pools提供的PoolableObjectFactory,針對pool池的管理操作介面
 

Java程式碼 
public interface PoolableObjectFactory {  
 
  Object makeObject() throws Exception;  
 
  void destroyObject(Object obj) throws Exception;  
 
  boolean validateObject(Object obj);  
 
  void activateObject(Object obj) throws Exception;  
 
  void passivateObject(Object obj) throws Exception;  

public interface PoolableObjectFactory {

  Object makeObject() throws Exception;

  void destroyObject(Object obj) throws Exception;

  boolean validateObject(Object obj);

  void activateObject(Object obj) throws Exception;

  void passivateObject(Object obj) throws Exception;
}
 

2. dbcp實現的pool從池管理操作

這裡貼了一個相關validate程式碼,具體類可見:PoolableConnectionFactory.validateConnection()

Java程式碼 
public class PoolableConnectionFactory implements PoolableObjectFactory {  
 
......  
public boolean validateObject(Object obj) { //驗證validateObject  
        if(obj instanceof Connection) {  
            try {  
                validateConnection((Connection) obj);  
                return true;  
            } catch(Exception e) {  
                return false;  
            
        } else {  
            return false;  
        
    
public void validateConnection(Connection conn) throws SQLException {  
        String query = _validationQuery;  
        if(conn.isClosed()) {  
            throw new SQLException("validateConnection: connection closed");  
        
        if(null != query) {  
            Statement stmt = null;  
            ResultSet rset = null;  
            try {