1.1 JDBC數據庫連接池的必要性
-
在使用開發基於數據庫的web程序時,傳統的模式基本是按以下步驟:
- 在主程序(如servlet、beans)中建立數據庫連接
- 進行sql操作
- 斷開數據庫連接
-
這種模式開發,存在的問題:
- 普通的JDBC數據庫連接使用 DriverManager 來獲取,每次向數據庫建立連接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)。需要數據庫連接的時候,就向數據庫要求一個,執行完成后再斷開連接。這樣的方式將會消耗大量的資源和時間。數據庫的連接資源並沒有得到很好的重復利用。若同時有幾百人甚至幾千人在線,頻繁的進行數據庫連接操作將占用很多的系統資源,嚴重的甚至會造成服務器的崩潰。
- 對於每一次數據庫連接,使用完后都得斷開。否則,如果程序出現異常而未能關閉,將會導致數據庫系統中的內存泄漏,最終將導致重啟數據庫。(回憶:何為Java的內存泄漏?)
- 這種開發不能控制被創建的連接對象數,系統資源會被毫無顧及的分配出去,如連接過多,也可能導致內存泄漏,服務器崩潰。
1.2 數據庫連接池技術
-
為解決傳統開發中的數據庫連接問題,可以采用數據庫連接池技術。
-
數據庫連接池的基本思想:就是為數據庫連接建立一個“緩沖池”。預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,只需從“緩沖池”中取出一個,使用完畢之后再放回去。
-
數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個。
-
數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量是由最小數據庫連接數來設定的。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這么多的連接數量。連接池的最大數據庫連接數量限定了這個連接池能占有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。

- 工作原理:

-
數據庫連接池技術的優點
1. 資源重用
由於數據庫連接得以重用,避免了頻繁創建,釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增加了系統運行環境的平穩性。
2. 更快的系統反應速度
數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置於連接池中備用。此時連接的初始化工作均已完成。對於業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而減少了系統的響應時間
3. 新的資源分配手段
對於多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接池的配置,實現某一應用最大可用數據庫連接數的限制,避免某一應用獨占所有的數據庫資源
4. 統一的連接管理,避免數據庫連接泄漏
在較為完善的數據庫連接池實現中,可根據預先的占用超時設定,強制回收被占用連接,從而避免了常規數據庫連接操作中可能出現的資源泄露
1.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()並沒有關閉數據庫的物理連接,它僅僅把數據庫連接釋放,歸還給了數據庫連接池。
1.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>
1.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
#...
1.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/使用ConfigFilter | |
| 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,是組合關系,並非替換關系 |
