羅金濤 李躍新
[摘要]數(shù)據(jù)庫連接池是一種非常高效實用的技術(shù)。對數(shù)據(jù)庫連接池的基本實現(xiàn)原理進行分析,并給出初步的算法實現(xiàn)。
[關(guān)鍵詞]數(shù)據(jù)源 連接池 連接 JAVA JDBC
中圖分類號:TP3文獻標識碼:A文章編號:1671-7597(2009)0310052-02
一、引言
在任何的項目開發(fā)過程中,都離不開對數(shù)據(jù)的操作,這一系列的動作最終都體現(xiàn)在底層和數(shù)據(jù)庫的交互,在常規(guī)的jdbc數(shù)據(jù)庫操作到時候,總是要反復(fù)的打開和關(guān)閉對數(shù)據(jù)庫的連接,但是這個過程是一個相當消耗系統(tǒng)資源的過程,對于小的一般程序環(huán)境來說,似乎感覺不到性能的影響,但是對于一些多層結(jié)構(gòu)大型企業(yè)級的應(yīng)用程序環(huán)境來說,這種反復(fù)消耗系統(tǒng)資源的弊端就開始體現(xiàn)出來了。于是很多服務(wù)器產(chǎn)品都提供了連接池技術(shù)來提高數(shù)據(jù)庫操作的性能,通過連接池技術(shù),可以盡可能多對內(nèi)存資源進行重用,大大節(jié)約了內(nèi)存的開銷,同時能夠支持更多的客戶服務(wù)和提高程序的運行效率,最終從整體上提高到了服務(wù)器的運行效率。
二、連接池的產(chǎn)生及基本原理
在早期的java項目開發(fā)過程中,當項目開發(fā)完成之后,運行的時候發(fā)現(xiàn)隨著訪問量的增長,系統(tǒng)的性能下降得特別的嚴重,實踐表面,導致系統(tǒng)性能下降的原因就是發(fā)生在數(shù)據(jù)庫訪問階段,而在此階段,有一個反復(fù)執(zhí)行的動作,那就是建立和關(guān)閉Connection對象,由于關(guān)閉操作是在所有的數(shù)據(jù)庫動作執(zhí)行之后才進行,它主要目的是釋放資源,而建立Connection對象的操作是在加載了數(shù)據(jù)庫驅(qū)動之后,數(shù)據(jù)庫操作之前必須完成的動作,而且這個對象的生成過程比較耗時,如果每次操作數(shù)據(jù)庫都臨時生成一個Connection對象,那么隨著并發(fā)訪問量的增加,必然會影響系統(tǒng)的性能,所以Connection對象的生成就是影響系統(tǒng)性能的主要原因,為了解決這個瓶頸,一個普遍可行的解決方案,就是在應(yīng)用啟動的時候,一次性生成若干個Connection對象,而不是在每次操作數(shù)據(jù)庫的時候去臨時生成,這些一次性生成的若干個Connection對象就可以常駐內(nèi)存中可以反復(fù)被使用,這樣就避免了Connection對象的創(chuàng)建過程耗時的缺陷,從而使得數(shù)據(jù)庫訪問速度得到了很大的提升,瓶頸有效的得到了緩解,這就是數(shù)據(jù)庫連接池技術(shù)的產(chǎn)生背景。
圖1顯示了數(shù)據(jù)庫連接池技術(shù)的基本原理。
當客戶端訪問需要對數(shù)據(jù)庫進行請求,需要先建立數(shù)據(jù)庫的連接對象Connection,此時是向數(shù)據(jù)源對象(DataSource)進行請求,而數(shù)據(jù)源對象預(yù)先一次性和數(shù)據(jù)庫(Database)建立好了若干個連接(Connection),并將這些連接組成一個連接池(Connection Pool),由應(yīng)用程序動態(tài)的對連接池中的連接進行申請,使用和釋放。當請求的對象用完以后,不需要進行關(guān)閉,直接返回給連接池當中,以便其他請求可以重復(fù)使用。當并發(fā)的請求的數(shù)量多于連接池的連接數(shù)的時候,這些請求先在排隊請求隊列當中排隊等候,然后由應(yīng)用程序根據(jù)連接池中的使用情況,動態(tài)的來增加連接數(shù)。
三、數(shù)據(jù)庫連接池基本功能的算法實現(xiàn)
因為連接池的建立已經(jīng)在它基礎(chǔ)上的操作和對集合類型的操作非常相似,因此我們可以將整個實現(xiàn)過程通過對集合類型的對象操作來進行模擬實現(xiàn),首先,我們要建立一個數(shù)據(jù)庫連接池類ConnectionPool,為了控制在取得連接的過程中只生成一個類的實例,應(yīng)該用單態(tài)模式(Singleton)來設(shè)計這個連接池類,通過單態(tài)模式可以節(jié)省內(nèi)存的開銷,同時也降低了Java虛擬機(JVM)進行垃圾回收的開銷。首先需要定義一個私有的構(gòu)造方法,然后通過保留一個公開的靜態(tài)方法來取得這個類的實例,另外要有一個容器來保存生成的連接,在JAVA中,我們一般使用集合類型的對象,例如Vector,ArrayList都可以,但是考慮到多線程的安全性,我們一般使用Vector來進行封裝,其中用到的代碼片段如下:
首先定義一個名為ConnectionPool的類,里面用到的一系列屬性和幾個主要方法如下:
private Vector pool; //Vector類型的連接池對象
private String url; //數(shù)據(jù)庫訪問的url
private String username; //數(shù)據(jù)庫訪問的用戶名
private String password; //數(shù)據(jù)庫的密碼
private String driver; //數(shù)據(jù)庫的驅(qū)動類
private int poolSize; //連接池的大小,即連接的數(shù)量
private int initSize; //初始化連接的數(shù)量
private int poolSizeIncrement; //當連接數(shù)不夠時的容量的增量
private static ConnectionPool instance=null; //定義一個靜態(tài)的連接池類變量
//私有構(gòu)造方法,讀取屬性文件的內(nèi)容,建立指定數(shù)量的連接池中的初始連接。
private ConnectionPool(){
//讀取初始化配置參數(shù),我們可以將數(shù)據(jù)庫參數(shù)保存在properties的屬性文件中,或者在xml文件中加以配置,推薦使用xml方式,可以提高系統(tǒng)解耦。
… … … …
//實例化一個向量,當作數(shù)據(jù)庫連接池對象的容器。
Vector pool=new Vector(poolSize, poolSizeIncrement);
//在連接池中創(chuàng)建指定數(shù)目的數(shù)據(jù)庫連接對象
for(int i=0;i try{ Class.forName(driver); Connection conn=java.sql.DriverManager. getConnection(url,username,password); }catch (SQLException e) { e.printStackTrace(); }catch (ClassNotFoundException e) { e.printStackTrace(); } //將生成的Connection對象放入Vector中 pool.add(conn); } } //靜態(tài)方法,用于初始化該連接池類的實例。 public static ConnectionPool getInstance(){ if(instance==null){ instance=new ConnectionPool(); } return instance; } 當我們通過上面的方法調(diào)用這個類的私有構(gòu)造函數(shù)后,它就會根據(jù)配置文件中的參數(shù),創(chuàng)建指定數(shù)量的數(shù)據(jù)庫連接,如果有程序需要進行數(shù)據(jù)庫訪問,那么可以給它分配一個連接,因為所有的連接對象都保存在一個Vector中,對于每個需要取得連接的請求,首先就讓它取得get(0)位置上連接對象,并將它從Vector中刪除,從而保證Vector中剩下的都是可用的連接。相反,當一個連接用完以后,就應(yīng)該釋放這個連接,并且將這個剛剛釋放的連接重新加入到連接池Vector中,其具體代碼如下:
//返回連接池中的一個數(shù)據(jù)庫連接,用戶自己來調(diào)用以取得數(shù)據(jù)庫的連接對象。
public synchronized Connection getConnection(){
if(pool.size()>0){
Connection conn=(Connection)pool.get(0);
//取得連接后,從連接池中刪除
pool.remove(conn);
return conn;
}else{
return null;
}
}
//釋放連接方法,需要用戶自己來調(diào)用這個方法。
public synchronized void closeConnection(Connection conn){
//返回到連接池中,即將這個Conncetion對象加入到Vector中最前面。
pool.add(0,conn);
}
如果最后我們的要關(guān)閉所有的連接,那么就要直接關(guān)閉數(shù)據(jù)庫連接池,那么可以通過遍歷,依次連接池對象中的每個連接,同時可以從vector對象中移除,代碼如下所示:
publicsynchronized void closePool(){
//通過遍歷的方式依次關(guān)閉Conncetion對象
for(int i=0;i try{ ((Connection)pool.get(i)).close(); }catch(SQLException e){ e.printStackTrace(); } //從Vector中移除 pool.remove(i); } } 以上就是基于數(shù)據(jù)庫連接池技術(shù)的基本原理及用java代碼的算法實現(xiàn)過程,當我們需要使用連接池技術(shù)時,首先我們通過ConnectionPool.getI nstance()方法取得連接池實例,然后會自動調(diào)用私有的構(gòu)造方法ConnectionPool()取得相應(yīng)數(shù)據(jù)庫連接參數(shù),并在Vector中生成指定數(shù)目的Conncetion對象,在程序中我們就可以通過調(diào)用getConnection()方法來取得連接了,因為此時的連接不是臨時生成的,而是在連接池初始化時就生成了,所以這個取得Conncetion對象的過程的效率非常高,最后我們可以調(diào)用closeConnection(Connection conn)和closePool()方法依次關(guān)閉Conncetion和連接池。測試表明,通過連接池技術(shù)的來訪問數(shù)據(jù)庫比不使用連接池技術(shù)時效率要高很多,當數(shù)據(jù)庫訪問量大時,采用連接池技術(shù)的優(yōu)勢更加明顯。 目前可用的數(shù)據(jù)庫連接池組件也很多,例如C3PO,DBCP,PROXOOL等都是一些優(yōu)秀的連接池組件,我們只需要在程序中引入相關(guān)的類庫,然后通過相關(guān)的參數(shù)配置或者實現(xiàn)相應(yīng)的接口和方法,都可以很方便地在實際的項目中使用它們提供的數(shù)據(jù)庫連接池技術(shù)了,另外在很多服務(wù)器中,如Tomcat,Jboss,WebLogic,WebSphere都內(nèi)置提供了對數(shù)據(jù)庫連接池的支持,例如在Tomcat中本身也帶有連接池的功能,它是通過配置數(shù)據(jù)源(DataSource)參數(shù)來實現(xiàn)的連接池功能,通過在配置文件的相應(yīng)位置加入如下代碼 DataSource ds = (DataSource)context.lookup(""); con=ds.getConnection(); 值得特別注意的是,有了這個連接,我們可以像平時一樣操作數(shù)據(jù)庫了,而且我們可以執(zhí)行con.close();讓連接池收回這個連接,但和普通連接不同的是,此時并沒有關(guān)閉到數(shù)據(jù)庫的物理連接,所以下次請求的時候不會重新生成新的連接,這也是使用連接池技術(shù)的好處,以上數(shù)據(jù)源的配置也是建立在連接池技術(shù)的基本原理之上的,例如,name,username,passw Ord,driverClassName,url等參數(shù)實際上就是和數(shù)據(jù)庫訪問相關(guān)的參數(shù),另外,maxActive代表最大連接數(shù),maxIdle表示最大空閑數(shù),maxWait代表最大等待數(shù),這些額外的參數(shù)只是各個服務(wù)器廠商提供的連接池的額外屬性。 四、結(jié)束語 本文是從數(shù)據(jù)庫連接池最基本的原理著手,從最基本的層面分析了數(shù)據(jù)庫連接池技術(shù)的核心原理,并用Java語言實現(xiàn)了核心代碼,目前的數(shù)據(jù)庫連接池組件很豐富,功能也各不相同,但是其核心原理都差不多,都是在數(shù)據(jù)庫連接池技術(shù)的最基本的功能上的不斷完善與創(chuàng)新。 參考文獻: [1]閻宏,Java與模式[M].北京:電子工業(yè)出版社,2002年10月.209-227. [2]李剛,輕量級J2EE企業(yè)應(yīng)用實戰(zhàn)[M].北京:電子工業(yè)出版社,2007年04月.390-399. [3]劉曉華、張健、周慧貞,JSP應(yīng)用開發(fā)詳解[M].北京:電子工業(yè)出版社,2007年07月.323-327. 作者簡介: 羅金濤,男,漢族,湖北咸寧人,碩士,研究方向:人工智能,并行計算;李躍新,男,漢族,湖北武漢人,副教授,研究方向:人工智能,并行計算。