連結池原理

1. 概述

連結池技術的核心思想是:連結複用,透過建立一個 資料庫連結池 以及一套連結使用、分配、管理策略,使得該 連結池 中的連結可以得到高效、安全的複用,避免了資料庫連結頻繁建立、關閉的花費。另外,對 JDBC 中的原始連結進行了封裝,會方便資料庫應用對於連結的使用(特別是對於異動處理),提高了獲取資料庫連結效率,也正是因為這個封裝層的存在,隔離了應用的本身的處理邏輯和具體資料庫存取邏輯,使應用本身的複用成為可能。

連結池主要由三部分組成:連結池的建立、連結池中連結使用的治理、連結池的關閉。

2. 連結池原理

2.1 連結池的建立

應用程式中建立的連結池其實是一個靜態的。所謂靜態連結池是指連結池中的連結在系統初始化時就已分配好,且不能隨意關閉連結。Java中提供了很多容器類可以方便的構建連結池,如:Vector、Stack、Servlet、Bean 等,透過讀取連結屬性檔案 Connections.properties 與資料庫實體建立連結。在系統初始化時,根據相應的配置建立連結並放置在連結池中,以便需要使用時能從連結池中獲取,這樣就可以避免連結隨意的建立、關閉造成的花費。

2.2 連結池的管理

連結池管理策略是連結池機制的核心。當連結池建立後,如何對連結池中的連結進行管理,解決好連結池內連結的分配和釋放,對系統的效能有很大的影響。連結的合理分配、釋放可提高連結的複用,降低了系統建立新連結的花費,同時也加速了使用者的存取速度。下面介紹連結池中連結的分配、釋放策略。

連結池的分配、釋放策略對於有效複用連結非常重要,我們採用的方法是一個很有名的設計模式:Reference Counting(引用記數)。該模式在複用資源方面應用的非常廣泛,把該方法運用到對於連結的分配釋放上,為每一個資料庫連結,保留一個引用記數,用來記錄該連結的使用者的個數。

具體實現方法如下:

當客戶請求資料庫連結時,首先查看連結池中是否有閒置連結(指當前沒有分配出去的連結)。如果存在閒置連結,則把連結分配給客戶並作相應處理(即標記該連結為正在使用,引用計數加 1)。如果沒有閒置連結,則查看當前所開的連結數是不是已經達到 maxConn(最大連結數),如果沒達到就重建立立一個連結給請求的客戶;如果達到就按設定的maxWaitTime(最大等待時間)進行等待,如果等待 maxWaitTime 後仍沒有閒置連結,就丟擲無閒置連結的異常給使用者。

當客戶釋放資料庫連結時,先判斷該連結的引用次數是否超過了規定值,如果超過就刪除該連結,並判斷當前連結池內總的連結數是否小於 minConn(最小連結數),若小於就將連結池充滿;如果沒超過就將該連結標記為開放狀態,可供再次複用。可以看出正是這套策略保證了資料庫連結的有效複用,避免頻繁地建立、釋放連結所帶來的系統資源花費。

2.3 連結池的關閉

當應用程式跳出時,應關閉連結池,此時應把在連結池建立時向資料庫申請的連結物件統一歸還給資料庫(即關閉所有資料庫連結),這與連結池的建立正好是一個相反程式。

連結池分配一個連結後如定義一個資料集,點選預覽,執行完對應的sql語句會將所佔用的連結歸還連結池。

3. 連結池的配置

資料庫連結池中到底要放置多少個連結,才能使系統的效能更佳,用 minConn 和 maxConn 來限制。

minConn 是當應用啟動的時候連結池所建立的連結數,假如過大啟動將變慢,但是啟動後回应更快;假如過小啟動加快,但是最初使用的使用者將因為連結池中沒有足夠的連結不可避免的延緩了執行速度。因此應該在開發的程式中設定較小 minConn,而在實際應用的中設定較大 minConn。maxConn 是連結池中的最大連結數,可以透過反覆試驗來確定此飽和點。

為此在連結池類 ConnectionPool 中加入兩個方法 getActiveSize() 和 getOpenSize(),ActiveSize 表示某一時間有多少連結正被使用,OpenSize 表示連結池中有多少連結被開啟,反映了連結池使用的峯值。將這兩個值在日誌資訊中反應出來, minConn 的值應該小於平均 ActiveSize,而 maxConn 的值應該在 activeSize 和 OpenSize 之間。

4. 連結池的關鍵技術

4.1 異動處理

前面討論的是關於使用資料庫連結進行普通的資料庫存取。對於 異動處理,情況就變得比較複雜。因為交易本身要求原子性的保證,此時就要求對於資料庫的運算子合"All-or-Nothing"原則,即要麼全部完成,要麼什麼都不做。如果簡單的採用上述的連結複用的策略,就會發生問題,因為沒有辦法控制屬於同一個交易的多個資料庫操作方法的動作,可能這些資料庫操作是在多個連結上進行的,並且這些連結可能被其他非交易方法複用。Connection本身具有提供了對於交易的支援,可以透過設定Connection的AutoCommit屬性為false,顯式的呼叫 commit或rollback方法來實現。但是要安全、高效的進行連結複用,就必須提供相應的交易支援機制。方法是:採用顯式的交易支撐方法,每一個交易獨佔一個連結。這種方法可以大大降低對於 異動處理 的複雜性,並且又不會妨礙連結的複用。連結管理服務提供了顯式的 交易 開始、結束(commit 或rollback)宣告,以及一個交易 註冊表 ,用於登記交易發起者和交易使用的連結的對應關係,透過該表,使用交易的部分和連結管理部分就隔離開,因為該表是在運作時根據實際的呼叫情況動態生成的。交易使用的連結在該交易運作中不能被複用。在實現中,使用者標識 是透過使用者所在的執行緒來標識的。後面的所有對於資料庫的存取都是透過尋找該註冊表,使用已經分配的連結來完成的。當 交易 結束時,從註冊表中刪除相應表項。

4.2 併發

為了使連結管理服務有更大的通用性,我們必須要考慮到多執行緒環境,即併發問題。在一個多執行緒的環境下,必須要保證連結管理自身資料的一致和連結內部資料的一致,在這方面Java提供很好的支援(synchronized關鍵字),這樣就很容易使連結管理成為執行緒安全的。

4.3 多資料庫伺服器

在實際應用中,應用程式常常需要存取多個不同的資料庫。如何透過同一個 連結池 存取不同的資料庫,是應用程式需要解決的一個核心問題。下面介紹一種解決的途徑:首先,定義一個 資料庫連結池 參數的類,定義了資料庫的 JDBC 驅動程式類名,連結的URL以及帳號通行碼等等一些資訊,該類是用於初始化連結池的參數:public class ConnectionParam implements Serializable{//各初始化參數的定義}其次是連結池的 工廠類 ConnectionFactory,透過該類將一個連結池物件與一個名稱對應起來,使用者透過該名稱就可以獲取指定的 連結池 物件,實現的主要程式碼如下:

public class ConnectionParam implements Serializable{
  //各初始化參數的定義
}

其次是連結池的工廠類ConnectionFactory,透過該類將一個連結池物件與一個名稱對應起來,使用者透過該名稱就可以獲取指定的連結池物件,實現的主要程式碼如下:

public class ConnectionFactory{
  static Hashtable connectionPools = //用來儲存資料源名和連結池物件的關係
  public static DataSource lookup(String dataSourceName) throws NameNotFoundException{
    //尋找名稱為dataSourceName的資料源
  }
  public static DataSource bind(Stringname, ConnectionParam param) throws Exception{
    //將名稱name與使用param初始化的連結池物件綁定
  }
}

public static void unbind(String name) throws NameNotFound Exception{
//將與名稱name綁定的連結池物件刪除
}


5. 連結池應用的實現

一個完整的 連結池 應用包括三個部分:DBConnectionPool類,負責從連結池獲取(或建立)連結、將連結傳回給連結池、系統關閉時關閉所有連結釋放所有資源;DBConnectionManager類,負責裝載和註冊 JDBC 驅動、根據屬性檔案中定義的屬性建立DBConnectionPool、磁軌應用程式對連結池的引用等;應用程式對連結池的使用。

本文實現的 資料庫連結池 包括一個管理類DBConnectionManager,負責提供與多個連結池物件(DBConnectionPool類)之間的API。每一個連結池物件管理一組封裝過的JDBC連結物件Conn,封裝過的 JDBC 連結物件Conn可以被任意數量的Model層的組件共享。

類Conn 的設計很簡單,如下所示:


Class Conn {
Private java. sgl .Connection con; //資料庫連結物件
Public Boolean inUse ; //是否被使用
Public long lastAccess; //最近一次釋放該連結的時間
Public int useCount; // 被使用次數
}
下面是實現連結池的主要程式碼:
// 初始化資料庫連結池
public static synchronized void FastInitPool()
throws Exception {
try { Class.forName(driver);
for (int i=0; i<size; i++) {
Connection con = createConnection();
if (con!=null) addConnection(con);
} } }
// 向連結池物件中新增資料庫連結
private static void addConnection(Connection con) {
if (pool==null||pool1==null) {
pool=new Vector(size);
pool1=new Vector(size); }
pool.addElement(con);
pool1.addElement("false"); }
// 獲取資料庫連結
public static synchronized Connection getConn()
throws Exception {
Connection conn = null;
try { if (driver = null)
FastInitPool();
// 獲得一個可用的(閒置的)連結
for (int i = 0; i < pool.size(); i++) {
conn = (Connection)pool.elementAt(i);
if (pool1.elementAt(i)=="false") {
pool1.set(i,"true");
//System.out.println("從連結池中獲取第"+(i+1)+"個閒置連結");
return conn;
}
}
//如果沒有可用連結,且已有連結數小於最大連結數限制,則建立並增加一個新連結到連結池
conn = createConnection();
pool.addElement(conn);
pool1.addElement("true");
// System.out.println(" 所有連結都在使用,在連結池中再建立一個新連結");
}
catch (Exception e) {
System.err.println(e.getMessage());
throw new Exception(e.getMessage());
}
return conn; //傳回一個有效的新連結
}
public Connection getConnection(String strDriver, String strUrl, String strUserName, String strPassWord)
throws SQLException{
try{ Class.forName(strDriver);
conn = DriverManager.getConnection(strUrl, strUserName, strPassWord); }
return conn; }
显示代码


附件列表


主題: 資料準備
已經是第一篇
已經是最後一篇
  • 有幫助
  • 沒幫助
  • 只是瀏覽
中文(繁體)

滑鼠選中內容,快速回饋問題

滑鼠選中存在疑惑的內容,即可快速回饋問題,我們將會跟進處理。

不再提示

9s后關閉

獲取幫助
線上支援
獲取專業技術支援,快速幫助您解決問題
工作日9:00-12:00,13:30-17:30在线
頁面反饋
針對當前網頁的建議、問題反饋
售前咨詢
業務咨詢
電話:0933-790886或 0989-092892
郵箱:taiwan@fanruan.com
頁面反饋
*問題分類
不能為空
問題描述
0/1000
不能為空

反馈已提交

网络繁忙

反饋已提交

網絡繁忙