一、JDBC數(shù)據(jù)庫連接池的必要性
1、在開發(fā)基于數(shù)據(jù)庫的web程序時,傳統(tǒng)的模式基本是按以下步驟:
(1)在主程序中建立數(shù)據(jù)庫連接
(2)進行sql操作
(3)斷開數(shù)據(jù)庫連接
2、這種模式開發(fā),存在的問題:
(1)普通的JDBC數(shù)據(jù)庫連接使用 DriverManager 來獲取,每次向數(shù)據(jù)庫建立連接的時候都要將 Connection加載到內(nèi)存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)。需要數(shù)據(jù)庫連接的時候,就向數(shù)據(jù)庫要求一個,執(zhí)行完成后再斷開連接。這樣的方式將會消耗大量的資源和時間。數(shù)據(jù)庫的連接資源并沒有得到很好的重復(fù)利用。若同時有幾百人甚至幾千人在線,頻繁的進行數(shù)據(jù)庫連接操作將占用很多的系統(tǒng)資源,嚴重的甚至?xí)斐煞?wù)器的崩潰。
(2)對于每一次數(shù)據(jù)庫連接,使用完后都得斷開。否則,如果程序出現(xiàn)異常而未能關(guān)閉,將會導(dǎo)致數(shù)據(jù)庫系統(tǒng)中的內(nèi)存泄漏,最終將導(dǎo)致重啟數(shù)據(jù)庫
(3)這種開發(fā)不能控制被創(chuàng)建的連接對象數(shù),系統(tǒng)資源會被毫無顧及的分配出去,如連接過多,也可能導(dǎo)致內(nèi)存泄漏,服務(wù)器崩潰。
二、數(shù)據(jù)庫連接池技術(shù)
1、數(shù)據(jù)庫連接池的基本思想:
就是為數(shù)據(jù)庫連接建立一個“緩沖池”。預(yù)先在緩沖池中放入一定數(shù)量的連接,當(dāng)需要建立數(shù)據(jù)庫連接時,只需從“緩沖池”中取出一個,使用完畢之后再放回去。
數(shù)據(jù)庫連接池負責(zé)分配、管理和釋放數(shù)據(jù)庫連接,它允許應(yīng)用程序重復(fù)使用一個現(xiàn)有的數(shù)據(jù)庫連接,而不是重新建立一個。
數(shù)據(jù)庫連接池在初始化時將創(chuàng)建一定數(shù)量的數(shù)據(jù)庫連接放到連接池中,這些數(shù)據(jù)庫連接的數(shù)量是由最小數(shù)據(jù)庫連接數(shù)來設(shè)定的。無論這些數(shù)據(jù)庫連接是否被使用,連接池都將一直保證至少擁有這么多的連接數(shù)量。連接池的最大數(shù)據(jù)庫連接數(shù)量限定了這個連接池能占有的最大連接數(shù),當(dāng)應(yīng)用程序向連接池請求的連接數(shù)超過最大連接數(shù)量時,這些請求將被加入到等待隊列中。
三、多種開源的數(shù)據(jù)庫連接池
1、JDBC 的數(shù)據(jù)庫連接池使用 javax.sql.DataSource 來表示,DataSource 只是一個接口,該接口通常由web服務(wù)器(Weblogic, WebSphere, Tomcat)提供實現(xiàn)。也有一些開源組織實現(xiàn)的連接池框架,知名的如下:
(1)DBCP 是Apache提供的數(shù)據(jù)庫連接池。tomcat服務(wù)器自帶dbcp1.0數(shù)據(jù)庫連接池。速度相對c3p0較快,但因自身存在BUG,Hibernate3已不再提供支持,tomcat7開始重新開發(fā)高并發(fā)連接池取代了dbcp1.0。
(2)C3P0 是一個開源組織提供的一個數(shù)據(jù)庫連接池,速度相對較慢,穩(wěn)定性還可以,hibernate官方推薦使用。
(3)BoneCP 是一個開源組織提供的數(shù)據(jù)庫連接池,速度快。
(4)Druid 是阿里提供的數(shù)據(jù)庫連接池,據(jù)說是集DBCP 、C3P0 、Proxool優(yōu)點于一身的數(shù)據(jù)庫連接池,但是速度不確定是否有BoneCP快。
(5)HikariCP是目前使用最廣泛的連接池, (pronounced Hi-ka-lee)
2、DataSource 通常被稱為數(shù)據(jù)源,它包含連接池和連接池管理兩個部分,習(xí)慣上也經(jīng)常把 DataSource 稱為連接池
3、DataSource用來取代DriverManager來獲取Connection,獲取速度快,同時可以大幅度提高數(shù)據(jù)庫訪問速度。
特別注意:
(1)數(shù)據(jù)源和數(shù)據(jù)庫連接不同,數(shù)據(jù)源無需創(chuàng)建多個,它是產(chǎn)生數(shù)據(jù)庫連接的工廠,因此整個應(yīng)用只需要一個數(shù)據(jù)源即可。
(2)當(dāng)數(shù)據(jù)庫訪問結(jié)束后,程序還是像以前一樣關(guān)閉數(shù)據(jù)庫連接:conn.close();但conn.close()并沒有關(guān)閉數(shù)據(jù)庫的物理連接,它僅僅把數(shù)據(jù)庫連接釋放,歸還給了數(shù)據(jù)庫連接池。
三、使用連接池
以HikariCP為例,要使用JDBC連接池,先添加HikariCP的依賴如下:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.1</version>
</dependency>
緊接著,我們需要創(chuàng)建一個DataSource
實例,這個實例就是連接池:
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("password");
config.addDataSourceProperty("connectionTimeout", "1000"); // 連接超時:1秒
config.addDataSourceProperty("idleTimeout", "60000"); // 空閑超時:60秒
config.addDataSourceProperty("maximumPoolSize", "10"); // 最大連接數(shù):10
DataSource ds = new HikariDataSource(config);
注意創(chuàng)建DataSource
也是一個非常昂貴的操作,所以通常DataSource
實例總是作為一個全局變量存儲,并貫穿整個應(yīng)用程序的生命周期。
有了連接池以后,我們?nèi)绾问褂盟兀亢颓懊娴拇a類似,只是獲取Connection
時,把DriverManage.getConnection()
改為ds.getConnection()
:
try (Connection conn = ds.getConnection()) { // 在此獲取連接
...
} // 在此“關(guān)閉”連接
通過連接池獲取連接時,并不需要指定JDBC的相關(guān)URL、用戶名、口令等信息,因為這些信息已經(jīng)存儲在連接池內(nèi)部了(創(chuàng)建HikariDataSource
時傳入的HikariConfig
持有這些信息)。一開始,連接池內(nèi)部并沒有連接,所以,第一次調(diào)用ds.getConnection()
,會迫使連接池內(nèi)部先創(chuàng)建一個Connection
,再返回給客戶端使用。當(dāng)我們調(diào)用conn.close()
方法時(在try(resource){...}
結(jié)束處),不是真正“關(guān)閉”連接,而是釋放到連接池中,以便下次獲取連接時能直接返回。
因此,連接池內(nèi)部維護了若干個Connection
實例,如果調(diào)用ds.getConnection()
,就選擇一個空閑連接,并標記它為“正在使用”然后返回,如果對Connection
調(diào)用close()
,那么就把連接再次標記為“空閑”從而等待下次調(diào)用。這樣一來,我們就通過連接池維護了少量連接,但可以頻繁地執(zhí)行大量的SQL語句。
通常連接池提供了大量的參數(shù)可以配置,例如,維護的最小、最大活動連接數(shù),指定一個連接在空閑一段時間后自動關(guān)閉等,需要根據(jù)應(yīng)用程序的負載合理地配置這些參數(shù)。此外,大多數(shù)連接池都提供了詳細的實時狀態(tài)以便進行監(jiān)控。