【JDBC第8章】數據庫鏈接池

第8章:數據庫鏈接池

8.1 JDBC數據庫鏈接池的必要性

  • 在使用開發基於數據庫的web程序時,傳統的模式基本是按如下步驟:  
    • 在主程序(如servlet、beans)中創建數據庫鏈接
    • 進行sql操做
    • 斷開數據庫鏈接
  • 這種模式開發,存在的問題:
    • 普通的JDBC數據庫鏈接使用 DriverManager 來獲取,每次向數據庫創建鏈接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)。須要數據庫鏈接的時候,就向數據庫要求一個,執行完成後再斷開鏈接。這樣的方式將會消耗大量的資源和時間。數據庫的鏈接資源並無獲得很好的重複利用。若同時有幾百人甚至幾千人在線,頻繁的進行數據庫鏈接操做將佔用不少的系統資源,嚴重的甚至會形成服務器的崩潰。
    • 對於每一次數據庫鏈接,使用完後都得斷開。不然,若是程序出現異常而未能關閉,將會致使數據庫系統中的內存泄漏,最終將致使重啓數據庫。(回憶:何爲Java的內存泄漏?)
    • 這種開發不能控制被建立的鏈接對象數,系統資源會被毫無顧及的分配出去,如鏈接過多,也可能致使內存泄漏,服務器崩潰。

8.2 數據庫鏈接池技術

  • 爲解決傳統開發中的數據庫鏈接問題,能夠採用數據庫鏈接池技術。
  • 數據庫鏈接池的基本思想:就是爲數據庫鏈接創建一個「緩衝池」。預先在緩衝池中放入必定數量的鏈接,當須要創建數據庫鏈接時,只需從「緩衝池」中取出一個,使用完畢以後再放回去。java

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

  • 工做原理:

  • 數據庫鏈接池技術的優勢git

    1. 資源重用github

    因爲數據庫鏈接得以重用,避免了頻繁建立,釋放鏈接引發的大量性能開銷。在減小系統消耗的基礎上,另外一方面也增長了系統運行環境的平穩性。web

    2. 更快的系統反應速度sql

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

    3. 新的資源分配手段緩存

    對於多應用共享同一數據庫的系統而言,可在應用層經過數據庫鏈接池的配置,實現某一應用最大可用數據庫鏈接數的限制,避免某一應用獨佔全部的數據庫資源tomcat

    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,是組合關係,並不是替換關係
相關文章
相關標籤/搜索