1. 程式人生 > 實用技巧 >【JDBC第8章】資料庫連線池

【JDBC第8章】資料庫連線池

第8章:資料庫連線池

8.1 JDBC資料庫連線池的必要性

  • 在使用開發基於資料庫的web程式時,傳統的模式基本是按以下步驟:  
    • 在主程式(如servlet、beans)中建立資料庫連線
    • 進行sql操作
    • 斷開資料庫連線
  • 這種模式開發,存在的問題:
    • 普通的JDBC資料庫連線使用 DriverManager 來獲取,每次向資料庫建立連線的時候都要將 Connection 載入到記憶體中,再驗證使用者名稱和密碼(得花費0.05s~1s的時間)。需要資料庫連線的時候,就向資料庫要求一個,執行完成後再斷開連線。這樣的方式將會消耗大量的資源和時間。資料庫的連線資源並沒有得到很好的重複利用。若同時有幾百人甚至幾千人線上,頻繁的進行資料庫連線操作將佔用很多的系統資源,嚴重的甚至會造成伺服器的崩潰。
    • 對於每一次資料庫連線,使用完後都得斷開。否則,如果程式出現異常而未能關閉,將會導致資料庫系統中的記憶體洩漏,最終將導致重啟資料庫。(回憶:何為Java的記憶體洩漏?)
    • 這種開發不能控制被建立的連線物件數,系統資源會被毫無顧及的分配出去,如連線過多,也可能導致記憶體洩漏,伺服器崩潰。

8.2 資料庫連線池技術

  • 為解決傳統開發中的資料庫連線問題,可以採用資料庫連線池技術。
  • 資料庫連線池的基本思想:就是為資料庫連線建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。

  • 資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是重新建立一個
  • 資料庫連線池在初始化時將建立一定數量的資料庫連線放到連線池中,這些資料庫連線的數量是由最小資料庫連線數來設定的。無論這些資料庫連線是否被使用,連線池都將一直保證至少擁有這麼多的連線數量。連線池的最大資料庫連線數量限定了這個連線池能佔有的最大連線數,當應用程式向連線池請求的連線數超過最大連線數量時,這些請求將被加入到等待佇列中。

  • 工作原理:

  • 資料庫連線池技術的優點

    1. 資源重用

    由於資料庫連線得以重用,避免了頻繁建立,釋放連線引起的大量效能開銷。在減少系統消耗的基礎上,另一方面也增加了系統執行環境的平穩性。

    2. 更快的系統反應速度

    資料庫連線池在初始化過程中,往往已經建立了若干資料庫連線置於連線池中備用。此時連線的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連線,避免了資料庫連線初始化和釋放過程的時間開銷,從而減少了系統的響應時間

    3. 新的資源分配手段

    對於多應用共享同一資料庫的系統而言,可在應用層通過資料庫連線池的配置,實現某一應用最大可用資料庫連線數的限制,避免某一應用獨佔所有的資料庫資源

    4. 統一的連線管理,避免資料庫連線洩漏

    在較為完善的資料庫連線池實現中,可根據預先的佔用超時設定,強制回收被佔用連線,從而避免了常規資料庫連線操作中可能出現的資源洩露

8.3 多種開源的資料庫連線池

  • JDBC 的資料庫連線池使用 javax.sql.DataSource 來表示,DataSource 只是一個介面,該介面通常由伺服器(Weblogic, WebSphere, Tomcat)提供實現,也有一些開源組織提供實現:
    • DBCP 是Apache提供的資料庫連線池。tomcat 伺服器自帶dbcp資料庫連線池。速度相對c3p0較快,但因自身存在BUG,Hibernate3已不再提供支援。
    • C3P0 是一個開源組織提供的一個數據庫連線池,速度相對較慢,穩定性還可以。hibernate官方推薦使用
    • Proxool 是sourceforge下的一個開源專案資料庫連線池,有監控連線池狀態的功能,穩定性較c3p0差一點
    • BoneCP 是一個開源組織提供的資料庫連線池,速度快
    • Druid 是阿里提供的資料庫連線池,據說是集DBCP 、C3P0 、Proxool 優點於一身的資料庫連線池,但是速度不確定是否有BoneCP快
  • DataSource 通常被稱為資料來源,它包含連線池和連線池管理兩個部分,習慣上也經常把 DataSource 稱為連線池
  • DataSource用來取代DriverManager來獲取Connection,獲取速度快,同時可以大幅度提高資料庫訪問速度。
  • 特別注意:
    • 資料來源和資料庫連線不同,資料來源無需建立多個,它是產生資料庫連線的工廠,因此整個應用只需要一個數據源即可。
    • 當資料庫訪問結束後,程式還是像以前一樣關閉資料庫連線:conn.close(); 但conn.close()並沒有關閉資料庫的物理連線,它僅僅把資料庫連線釋放,歸還給了資料庫連線池。

8.3.1 C3P0資料庫連線池

  • 獲取連線方式一
//使用C3P0資料庫連線池的方式,獲取資料庫的連線:不推薦
public static Connection getConnection1() throws Exception{
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass("com.mysql.jdbc.Driver"); 
    cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
    cpds.setUser("root");
    cpds.setPassword("abc123");
        
//  cpds.setMaxPoolSize(100);
    
    Connection conn = cpds.getConnection();
    return conn;
}
  • 獲取連線方式二
//使用C3P0資料庫連線池的配置檔案方式,獲取資料庫的連線:推薦
private static DataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection2() throws SQLException{
    Connection conn = cpds.getConnection();
    return conn;
}

其中,src下的配置檔案為:【c3p0-config.xml】

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <named-config name="helloc3p0">
        <!-- 獲取連線的4個基本資訊 -->
        <property name="user">root</property>
        <property name="password">abc123</property>
        <property name="jdbcUrl">jdbc:mysql:///test</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        
        <!-- 涉及到資料庫連線池的管理的相關屬性的設定 -->
        <!-- 若資料庫中連線數不足時, 一次向資料庫伺服器申請多少個連線 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化資料庫連線池時連線的數量 -->
        <property name="initialPoolSize">5</property>
        <!-- 資料庫連線池中的最小的資料庫連線數 -->
        <property name="minPoolSize">5</property>
        <!-- 資料庫連線池中的最大的資料庫連線數 -->
        <property name="maxPoolSize">10</property>
        <!-- C3P0 資料庫連線池可以維護的 Statement 的個數 -->
        <property name="maxStatements">20</property>
        <!-- 每個連線同時可以使用的 Statement 物件的個數 -->
        <property name="maxStatementsPerConnection">5</property>

    </named-config>
</c3p0-config>

8.3.2 DBCP資料庫連線池

  • DBCP 是 Apache 軟體基金組織下的開源連線池實現,該連線池依賴該組織下的另一個開源系統:Common-pool。如需使用該連線池實現,應在系統中增加如下兩個 jar 檔案:
    • Commons-dbcp.jar:連線池的實現
    • Commons-pool.jar:連線池實現的依賴庫
  • Tomcat 的連線池正是採用該連線池來實現的。該資料庫連線池既可以與應用伺服器整合使用,也可由應用程式獨立使用。
  • 資料來源和資料庫連線不同,資料來源無需建立多個,它是產生資料庫連線的工廠,因此整個應用只需要一個數據源即可。
  • 當資料庫訪問結束後,程式還是像以前一樣關閉資料庫連線:conn.close(); 但上面的程式碼並沒有關閉資料庫的物理連線,它僅僅把資料庫連線釋放,歸還給了資料庫連線池。
  • 配置屬性說明
屬性 預設值 說明
initialSize 0 連線池啟動時建立的初始化連線數量
maxActive 8 連線池中可同時連線的最大的連線數
maxIdle 8 連線池中最大的空閒的連線數,超過的空閒連線將被釋放,如果設定為負數表示不限制
minIdle 0 連線池中最小的空閒的連線數,低於這個數量會被建立新的連線。該引數越接近maxIdle,效能越好,因為連線的建立和銷燬,都是需要消耗資源的;但是不能太大。
maxWait 無限制 最大等待時間,當沒有可用連線時,連線池等待連線釋放的最大時間,超過該時間限制會丟擲異常,如果設定-1表示無限等待
poolPreparedStatements false 開啟池的Statement是否prepared
maxOpenPreparedStatements 無限制 開啟池的prepared 後的同時最大連線數
minEvictableIdleTimeMillis 連線池中連線,在時間段內一直空閒, 被逐出連線池的時間
removeAbandonedTimeout 300 超過時間限制,回收沒有用(廢棄)的連線
removeAbandoned false 超過removeAbandonedTimeout時間後,是否進 行沒用連線(廢棄)的回收
  • 獲取連線方式一:
public static Connection getConnection3() throws Exception {
    BasicDataSource source = new BasicDataSource();
        
    source.setDriverClassName("com.mysql.jdbc.Driver");
    source.setUrl("jdbc:mysql:///test");
    source.setUsername("root");
    source.setPassword("abc123");
        
    //
    source.setInitialSize(10);
        
    Connection conn = source.getConnection();
    return conn;
}
  • 獲取連線方式二:
//使用dbcp資料庫連線池的配置檔案方式,獲取資料庫的連線:推薦
private static DataSource source = null;
static{
    try {
        Properties pros = new Properties();
        
        InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
            
        pros.load(is);
        //根據提供的BasicDataSourceFactory建立對應的DataSource物件
        source = BasicDataSourceFactory.createDataSource(pros);
    } catch (Exception e) {
        e.printStackTrace();
    }
        
}
public static Connection getConnection4() throws Exception {
        
    Connection conn = source.getConnection();
    
    return conn;
}

其中,src下的配置檔案為:【dbcp.properties】

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=abc123

initialSize=10
#...

8.3.3 Druid(德魯伊)資料庫連線池

Druid是阿里巴巴開源平臺上一個資料庫連線池實現,它結合了C3P0、DBCP、Proxool等DB池的優點,同時加入了日誌監控,可以很好的監控DB池連線和SQL的執行情況,可以說是針對監控而生的DB連線池,可以說是目前最好的連線池之一。

package com.atguigu.druid;

import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class TestDruid {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();       pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        Connection conn = ds.getConnection();
        System.out.println(conn);
    }
}

其中,src下的配置檔案為:【druid.properties】

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=20
maxWait=1000
filters=wall
  • 詳細配置引數:
配置 預設 說明
name 配置這個屬性的意義在於,如果存在多個數據源,監控的時候可以通過名字來區分開來。 如果沒有配置,將會生成一個名字,格式是:”DataSource-” + System.identityHashCode(this)
url 連線資料庫的url,不同資料庫不一樣。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 連線資料庫的使用者名稱
password 連線資料庫的密碼。如果你不希望密碼直接寫在配置檔案中,可以使用ConfigFilter。詳細看這裡:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName 根據url自動識別 這一項可配可不配,如果不配置druid會根據url自動識別dbType,然後選擇相應的driverClassName(建議配置下)
initialSize 0 初始化時建立物理連線的個數。初始化發生在顯示呼叫init方法,或者第一次getConnection時
maxActive 8 最大連線池數量
maxIdle 8 已經不再使用,配置了也沒效果
minIdle 最小連線池數量
maxWait 獲取連線時最大等待時間,單位毫秒。配置了maxWait之後,預設啟用公平鎖,併發效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
poolPreparedStatements false 是否快取preparedStatement,也就是PSCache。PSCache對支援遊標的資料庫效能提升巨大,比如說oracle。在mysql下建議關閉。
maxOpenPreparedStatements -1 要啟用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改為true。在Druid中,不會存在Oracle下PSCache佔用記憶體過多的問題,可以把這個數值配置大一些,比如說100
validationQuery 用來檢測連線是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用。
testOnBorrow true 申請連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能。
testOnReturn false 歸還連線時執行validationQuery檢測連線是否有效,做了這個配置會降低效能
testWhileIdle false 建議配置為true,不影響效能,並且保證安全性。申請連線的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連線是否有效。
timeBetweenEvictionRunsMillis 有兩個含義: 1)Destroy執行緒會檢測連線的間隔時間2)testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明
numTestsPerEvictionRun 不再使用,一個DruidDataSource只支援一個EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls 物理連線初始化的時候執行的sql
exceptionSorter 根據dbType自動識別 當資料庫丟擲一些不可恢復的異常時,拋棄連線
filters 屬性型別是字串,通過別名的方式配置擴充套件外掛,常用的外掛有: 監控統計用的filter:stat日誌用的filter:log4j防禦sql注入的filter:wall
proxyFilters 型別是List,如果同時配置了filters和proxyFilters,是組合關係,並非替換關係