Connection Pool

數據庫連接池的基本原理是在內部對象池中維護一定數量的數據庫連接,并對外暴露數據庫連接獲取和返回方法。如:

外部使用者可通過getConnection 方法獲取連接,使用完畢后再通過releaseConnection 方法將連接返回,注意此時連接并沒有關閉,而是由連接池管理器回收,并為下一次使用做好準備。

數據庫連接池技術帶來的優勢:

1. 資源重用

由于數據庫連接得到重用,避免了頻繁創建、釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,另一方面也增進了系統運行環境的平穩性(減少內存碎片以及數據庫臨時進程/線程的數量)。

2. 更快的系統響應速度

數據庫連接池在初始化過程中,往往已經創建了若干數據庫連接置于池中備用。此時連接的初始化工作均已完成。對于業務請求處理而言,直接利用現有可用連接,避免了數據庫連接初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。

3. 新的資源分配手段

對于多應用共享同一數據庫的系統而言,可在應用層通過數據庫連接的配置,實現數據庫連接池技術,幾年錢也許還是個新鮮話題,對于目前的業務系統而言,如果設計中還沒有考慮到連接池的應用,那么…….快在設計文檔中加上這部分的內容吧。某一應用最大可用數據庫連接數的限制,避免某一應用獨占所有數據庫資源。

4. 統一的連接管理,避免數據庫連接泄漏

在較為完備的數據庫連接池實現中,可根據預先的連接占用超時設定,強制收回被占用連接。從而避免了常規數據庫連接操作中可能出現的資源泄漏。一個最小化的數據庫連接池實現:

public class DBConnectionPool implements ConnectionPool

{

private static Vector pool;

private final int POOL_MAX_SIZE = 20;

/*

* 獲取數據庫連接

* 如果當前池中有可用連接,則將池中最后一個返回,如果沒有,則新建一個返回

*/

public synchronized Connection getConnection() throws DBException

{

if (pool == null)

{

pool = new Vector();

}

Connection conn;

if (pool.isEmpty())

{

conn = createConnection();

}

else

{

int last_idx = pool.size() - 1;

conn = (Connection) pool.get(last_idx);

pool.remove(pool.get(last_idx));

}

return conn;

}

/*

* 將使用完畢的數據庫連接放回備用池。

*

* 判斷當前池中連接數是否已經超過閥值(POOL_MAX_SIZE),

* 如果超過,則關閉該連接。

* 否則放回池中以備下次重用。

*/

public synchronized void releaseConnection(Connection conn)

{

if (pool.size() > POOL_MAX_SIZE)

{

try

{

conn.close();

}

catch (SQLException e)

{

e.printStackTrace();

}

}

else

{

pool.add(conn);

}

}

/**

* 讀取數據庫配置信息,并從數據庫連接池中獲得數據庫連接

*

* @return

* @throws DBException

*/

private static Connection createConnection() throws DBException

{

Connection conn;

try

{

Class.forName("oracle.jdbc.driver.OracleDriver");

conn = DriverManager.getConnection(

"jdbc:oracle:thin:@localhost:1521:oracle",

"personal", "personal");

return conn;

}

catch (ClassNotFoundException e)

{

throw new DBException("ClassNotFoundException when loading JDBC Driver");

}

catch (SQLException e)

{

throw new DBException("SQLException when loading JDBC Driver");

}

}

}

上面的代碼實現了一個最簡單的連接池,因為簡單,所以明了,目的只是展示數據庫連接池實現的一般原理。完備的連接池實現固然錯綜復雜,但就其根本而言,還是源自同樣的思想。

先脫離連接池本身的具體實現,我們看看這段代碼在實際應用中可能產生的問題,注意到,由于getConnection 方法返回的是一個標準的JDBC Connection,程序員由于編程習慣,可能會習慣性的調用其close 方法關閉連接。如此一來,連接無法得到重用,數據庫連接池機制形同虛設。為了解決這個問題,比較好的途徑有:

1. Decorator 模式

2. Dynamic Proxy 模式

下面我們就這兩種模式進行一些探討

Decorator模式

“Decorator 模式的主要目的是利用一個對象,透明的為另一個對象添加新的功能”。這句話是從GOF 關于設計模式的經典著作《設計模式-可復用面向對象軟件的基礎》一書中關于Decorator 模式的描述直譯而來,可能比較難以理解。簡單來講,就是通過一個Decorator 對原有對象進行封裝,同時實現與原有對象相同的接口,從而得到一個基于原有對象的,對既有接口的增強性實現。其UML 描述如下:對于前面所討論的Connection 釋放的問題,理所當然,我們首先想到的是,如果能讓JDBC Connection 在執行close 操作時自動將自己返回到數據庫連接池中,那么所有問題都將迎刃而解,但是,JDBC Connection 自己顯然無法根據實際情況判斷何去何從。此時,引入Decorator 模式來解決我們所面對的問題就非常合適。
首先,我們引入一個ConnectionDecorator 類:
public class ConnectionDecorator implements Connection
{
Connection dbconn;
public ConnectionDecorator(Connection conn)
{
this.dbconn = conn; //實際從數據庫獲得的Connection引用
}
/* 此方法將被子類覆蓋,以實現數據庫連接池的連接返回操作
* @see java.sql.Connection#close()
*/
public void close() throws SQLException
{
this.dbconn.close();
}
/* (non-Javadoc)
* @see java.sql.Connection#commit()
*/
public void commit() throws SQLException
{
this.dbconn.commit();//調用實際連接的commit方法
}
……
//以下各個方法類似,均調用dbconn.xxx方法作為Connection接口定義的功能。
……
}
可以看到,ConnectionDecorator 類實際上是對傳入的數據庫連接加上了一個外殼,它實現了java.sql.Connection接口,不過本身并沒有實現任何實際內容,只是簡單的把方法的實現委托給運行期實際獲得的Connection 實例,而從外部來看,ConnectionDecorator與普通的Connection 實例并沒有什么區別,因為它們實現了同樣的接口,對外提供了同樣的功能調用。目標很清楚,通過這樣的封裝,我們的ConnectionDecorator 對于外部的程序員而言,調用方法與普通的JDBC Connection 完全相同,而在內部通過對ConnectionDecorator 的修改,我們就可以透明的改變現有實現,為之增加新的特性:
public class PooledConnection
extends ConnectionDecorator
implements Connection /????還需要實現這個接口嗎?
{
private ConnectionPool connPool;
public PooledConnection(ConnectionPool pool, Connection conn)
{
super(conn);
connPool = pool;
}
/**
* 覆蓋close方法,將數據庫連接返回連接池,而不是直接關閉連接
*/
public void close() throws SQLException
{
connPool.releaseConnection(this.dbconn);
}
……
}
為了應用新的PooledConnection,我們需要對原本的DBConnectionPool.getConnection 和releaseConnection 方法稍做改造:
public synchronized Connection getConnection() throws DBException
{
if (pool == null)
{
pool = new Vector();
}
Connection conn;
if (pool.isEmpty())
{
conn = createConnection();
}
else
{
int last_idx = pool.size() - 1;
conn = (Connection) pool.get(last_idx);
pool.remove(pool.get(last_idx));
}
return new PooledConnection(this,conn);
}
public synchronized void releaseConnection(Connection conn)
{
if (conn instanceof PooledConnection || pool.size() > POOL_MAX_SIZE)
{
try {
conn.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
else{
   pool.add(conn);
}

}

此時,獲取數據庫連接后,調用者只需要按照JDBC Connection 的標準用法進行調用即可,從而實現了數據庫連接池的透明化。同樣的道理,我們甚至可以利用Decorator模式對DriverManager 類進行同樣的改造,從而最小化數據庫連接池實現對傳統JDBC編碼方式的影響。有興趣的可以基于同樣的原理嘗試自行實現。