無論是怎樣的應(yīng)用系統(tǒng),都無法脫離對資源的管理和使用。而對于持久層而言,資源的合理管理和調(diào)度則顯得尤為重要。
在大多所應(yīng)用系統(tǒng)中,80%以上的應(yīng)用邏輯并不需要特別復(fù)雜的數(shù)據(jù)訪問邏輯(可能只是幾條簡單的Select或者Insert/Update語句)。對于這些占大多數(shù)的簡單邏輯而言,如果SQL語句和數(shù)據(jù)庫本身的設(shè)計不是太糟糕(合理的關(guān)聯(lián),字段索引以及數(shù)據(jù)庫分區(qū)策略),在特定的硬件環(huán)境下,我們認(rèn)為數(shù)據(jù)庫的性能基本穩(wěn)定,但對于大型業(yè)務(wù)系統(tǒng),即使在特定的硬件環(huán)境下,并且數(shù)據(jù)庫設(shè)計良好也會存在性能低下的問題。那么問題出在哪里呢?
由于每次數(shù)據(jù)庫訪問時都獲取一個獨(dú)立的數(shù)據(jù)庫連接,代碼在每個操作中都獨(dú)立獲取一個數(shù)據(jù)庫連接進(jìn)行操作,最后在依次關(guān)閉,可以想見,一個業(yè)務(wù)操作中如果多次重復(fù)這樣的過程,對于系統(tǒng)來說是多么大的性能開銷啊!
如何解決這樣的問題呢?我們使用數(shù)據(jù)庫連接池機(jī)制(Connection Pool)。
即使對于我們而言,通過JDBC獲取連接池實(shí)在是件再簡單不過的事情,但對于JDBC Driver來說,連接數(shù)據(jù)庫卻并非一件輕松差事,數(shù)據(jù)庫連接不僅僅是在應(yīng)用服務(wù)器與數(shù)據(jù)庫服務(wù)器之間建立一個Socket Connection(對于Type4的JDBC Driver而言),連接建立之后,應(yīng)用服務(wù)器和數(shù)據(jù)庫服務(wù)器之間需要交換若干次數(shù)據(jù)(驗(yàn)證用戶密碼、權(quán)限等),然后,數(shù)據(jù)庫開始初始化連接會話句柄,記錄聯(lián)機(jī)日志,為此連接分配相應(yīng)的處理進(jìn)程和系統(tǒng)資源。
系統(tǒng)如此忙碌,如果我們知識簡單扔過去兩個SQL語句,然后就將次連接拋棄,實(shí)在可惜,而數(shù)據(jù)庫連接池技術(shù)正是為了解決這個問題。
數(shù)據(jù)庫連接池的基本原理是在內(nèi)部對象池中維護(hù)一定數(shù)量的數(shù)據(jù)庫連接,并對外暴露數(shù)據(jù)庫連接獲取和返回方法。
外部使用者可通過getConnection方法獲取連接,使用完畢后再通過releaseConnection方法將連接返回,注意此時連接并沒有關(guān)閉,而是由連接池管理器收回,并為下一次使用做好準(zhǔn)備。
數(shù)據(jù)庫連接池技術(shù)帶來下面的優(yōu)勢:
1:資源重用
由于數(shù)據(jù)庫連接池連接得以重用,避免了頻繁創(chuàng)建,釋放連接引起的大量性能開銷。在減少系統(tǒng)消耗的基礎(chǔ)上,另一方面也增進(jìn)了系統(tǒng)運(yùn)行環(huán)境的平穩(wěn)性(減少內(nèi)存碎片以及數(shù)據(jù)庫臨時進(jìn)程/線程的數(shù)量)
2:更快的系統(tǒng)響應(yīng)速度
數(shù)據(jù)庫連接池在初始化過程中,往往已經(jīng)創(chuàng)建了若干數(shù)據(jù)庫連接置于池中備用。此時連接的初始化工作均已完成。對于業(yè)務(wù)請求處理而言,直接利用現(xiàn)有可用連接,避免了數(shù)據(jù)庫連接初始化和釋放過程的時間開銷,從而縮減了系統(tǒng)整體響應(yīng)時間。
3:新的資源分配手段
對于多應(yīng)用共享同一數(shù)據(jù)庫的系統(tǒng)而言,可在應(yīng)用層通過數(shù)據(jù)庫連接池的配置,實(shí)現(xiàn)某一應(yīng)用作大可用數(shù)據(jù)庫連接數(shù)的限制,避免某一應(yīng)用獨(dú)占所有數(shù)據(jù)庫資源。
4:統(tǒng)一的連接管理,避免數(shù)據(jù)庫連接泄露
在較為完備的數(shù)據(jù)庫連接池實(shí)現(xiàn)中,可根據(jù)預(yù)先的連接占用超時設(shè)定,強(qiáng)制收回被占用連接。從而避免了常規(guī)數(shù)據(jù)庫連接操作中可能出現(xiàn)的資源泄露。
接下來介紹一個連接池的簡單實(shí)現(xiàn)。
package com.phy.emis.db;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import java.util.Vector;


public final class Pool
{

private static String CONFIG = "db";
private static Vector vConnPool = new Vector();

private static int intMaxConnections = 20;
private static int iRetryTimes = 3;
private static String strDriver;
private static String strUrl;
private static String strUser;
private static String strPWD;
private static String strDataBase;
private static ResourceBundle rb;
public static boolean blnDebug;

private static int intClients = 0;
private static int intNew = 0, intGet = 0, intPut = 0;


static
{

try
{
rb = ResourceBundle.getBundle(CONFIG);
strDriver = rb.getString("driver"); //驅(qū)動程序字符串
strUrl = rb.getString("url"); //url字符串
strUser = rb.getString("user"); //登陸數(shù)據(jù)庫用戶名
strPWD = rb.getString("pwd"); //登陸數(shù)據(jù)庫密碼
strDataBase = rb.getString("database");


if (rb.getString("debug").equals("true"))
{
blnDebug = true;
}

else
{
blnDebug = false;

}
intMaxConnections = Integer.parseInt(rb.getString("MaxConnection"));
iRetryTimes = Integer.parseInt(rb.getString("DBRetryTimes"));

}

catch (Exception e)
{
System.out.println("不能讀取屬性文件" + e);
}
}

//新建一個數(shù)據(jù)庫連接

private static synchronized Connection newConnection()
{
intNew++;
Connection conn = null;
//System.out.println("new conn:" + intNew);
//初始化數(shù)據(jù)庫驅(qū)動程序

try
{
Driver cnnDriver = (Driver) Class.forName(strDriver).newInstance();
DriverManager.registerDriver(cnnDriver);
}

catch (Exception e)
{
System.out.println("error happened at init driver " + e);
}

//新建一個連接

try
{
conn = DriverManager.getConnection(strUrl, strUser, strPWD);
conn.setCatalog(strDataBase);
}

catch (Exception e)
{

if (blnDebug)
{
System.out.print(e.getMessage());
}
return null;
}

return conn;
}

//從連接池中獲得一個連接

public static synchronized Connection getConnection()
{
intGet++;
//System.out.println("get conn:" + intGet);
Connection conn = null;
int iIndex = -1;
boolean bClosed;
int iCnt = 0;
//System.out.println("new:"+intNew+" getCNN:"+intGet+" client:"+intClients);

while (iCnt < vConnPool.size())
{
conn = (Connection) vConnPool.elementAt(iCnt);


if (conn == null)
{ //連接為空,從連接池中移出
vConnPool.removeElementAt(iCnt);
continue;
}

else
{ //不為空

try
{


if (conn.isClosed())
{ //連接已經(jīng)被關(guān)閉,從連接池中移出
vConnPool.removeElementAt(iCnt);
continue;
}

else
{ //有效連接,得到連接在連接池中的位置
iIndex = iCnt;
break;
}
}

catch (Exception e)
{
vConnPool.removeElementAt(iCnt);
continue;
}
} //end else
} //end while


if (iIndex != -1)
{ //已經(jīng)得到連接,從連接池移出
vConnPool.removeElementAt(iIndex);
intClients++;
}

else
{

if (intClients < intMaxConnections)
{ //沒有得到連接:如果連接池未滿,新建一個連接
conn = newConnection(); //新建連接

if (conn != null)
{
intClients++;
}
}

else
{
return null;
}
} //end if

return conn;
}


/** *//*****
* 輔助方法,用于得到數(shù)據(jù)庫重試的次數(shù)
* @param none
* @return int 次數(shù)
*/

public static int getRetryTimes()
{
return iRetryTimes;
}

//將一個連接放入連接池中

public static synchronized void putConnection(Connection conn)
{
intPut++;
boolean bClosed;


try
{
bClosed = conn.isClosed();
}

catch (Exception e)
{
bClosed = true;
}


if (conn != null && !bClosed)
{
vConnPool.addElement(conn);
}


if (intClients > 0)
{
intClients--;
}
}

//回收所有的連接

protected void finalize()
{

try
{

for (int i = 0; i < vConnPool.size(); i++)
{
Connection conn = (Connection) vConnPool.elementAt(i);
conn.close();
}
}

catch (Exception e)
{
}
}

//打印當(dāng)前數(shù)據(jù)庫的連接池的相關(guān)信息,以便調(diào)試

public static String prtMsg(String strIn)
{
String strRet = "<< MSG >> intClients:"+intClients+" intNew:"+
intNew+" Get:"+intGet+" Put:"+intPut + " $$" + strIn ;

if(blnDebug)
{
System.out.println(strRet);
}
return strRet ;
}

//打印當(dāng)前數(shù)據(jù)庫的連接池的相關(guān)信息,以便調(diào)試

public static String prtMsg()
{
String strRet = "<< MSG >> intClients:"+intClients+" intNew:"+
intNew+" Get:"+intGet+" Put:"+intPut ;

if(blnDebug)
{
System.out.println(strRet);
}
return strRet ;
}


public static void processInvaildConn()
{

for(int i=0;i<vConnPool.size();i++)
{
Connection conn = (Connection) vConnPool.get(0);

try
{
conn.close();
vConnPool.remove(conn);
//intNew --;
}

catch(Exception e)
{
System.out.println("Error at processInvaildConn!"+e);
}
} //end for
System.out.println("From Pool:The connection has been reset by peer!");
intClients = 0;
intNew = 0;
intGet = 0;
intPut = 0;
System.out.println("From Pool:Init Connection!!");
}

}
