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

//新建一個數據庫連接

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

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())
{ //連接已經被關閉,從連接池中移出
vConnPool.removeElementAt(iCnt);
continue;
}

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

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


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

else
{

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

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

else
{
return null;
}
} //end if

return conn;
}


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

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)
{
}
}

//打印當前數據庫的連接池的相關信息,以便調試

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 ;
}

//打印當前數據庫的連接池的相關信息,以便調試

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!!");
}

}
