-
鏈接池技術
鏈接池技術的核心思想是:鏈接複用,經過創建一個數據庫鏈接池以及一套鏈接使用、分配、管理策略,使得該鏈接池中的鏈接能夠獲得高效、安全的複用,避免了數據庫鏈接頻繁創建、關閉的開銷。另外,因爲對JDBC中的原始鏈接進行了封裝,從而方便了數據庫應用對於鏈接的使用(特別是對於事務處理),提升了獲取數據庫鏈接效率,也正是由於這個封裝層的存在,隔離了應用的自己的處理邏輯和具體數據庫訪問邏輯,使應用自己的複用成爲可能。鏈接池主要由三部分組成:鏈接池的創建、鏈接池中鏈接的使用管理、鏈接池的關閉。
目錄
- 接池的創建
- 鏈接池的管理
- 鏈接池的關閉
- 鏈接池的配置
- 鏈接池的關鍵技術
- 事務處理
- 併發
- 多數據庫服務器
- 鏈接池應用的實現
鏈接池的創建
應用程序中創建的鏈接池實際上是一個靜態的。所謂靜態鏈接池是指鏈接池中的鏈接在系統初始化時就已分配好,且不能隨意關閉鏈接。Java中提供了不少容器類能夠方便的構建鏈接池,如:Vector、Stack、Servlet、Bean等,經過讀取鏈接屬性文件Connections.properties與數據庫實例創建鏈接。在系統初始化時,根據相應的配置建立鏈接並放置在鏈接池中,以便須要使用時能從鏈接池中獲取,這樣就能夠避免鏈接隨意的創建、關閉形成的開銷。
鏈接池的管理
鏈接池管理策略是鏈接池機制的核心。當鏈接池創建後,如何對鏈接池中的鏈接進行管理,解決好鏈接池內鏈接的分配和釋放,對系統的性能有很大的影響。鏈接的合理分配、釋放可提升鏈接的複用,下降了系統創建新鏈接的開銷,同時也加速了用戶的訪問速度。下面介紹鏈接池中鏈接的分配、釋放策略。
鏈接池的分配、釋放策略對於有效複用鏈接很是重要,咱們採用的方法是一個頗有名的設計模式:Reference Counting(引用記數)。該模式在複用資源方面應用的很是普遍,把該方法運用到對於鏈接的分配釋放上,爲每個數據庫鏈接,保留一個引用記數,用來記錄該鏈接的使用者的個數。具體的實現方法是:
當客戶請求數據庫鏈接時,首先查看鏈接池中是否有空閒鏈接(指當前沒有分配出去的鏈接)。若是存在空閒鏈接,則把鏈接分配給客戶並做相應處理(即標記該鏈接爲正在使用,引用計數加1)。若是沒有空閒鏈接,則查看當前所開的鏈接數是否是已經達到maxConn(最大鏈接數),若是沒達到就從新建立一個鏈接給請求的客戶;若是達到就按設定的maxWaitTime(最大等待時間)進行等待,若是等待maxWaitTime後仍沒有空閒鏈接,就拋出無空閒鏈接的異常給用戶。
當客戶釋放數據庫鏈接時,先判斷該鏈接的引用次數是否超過了規定值,若是超過就刪除該鏈接,並判斷當前鏈接池內總的鏈接數是否小於minConn(最小鏈接數),若小於就將鏈接池充滿;若是沒超過就將該鏈接標記爲開放狀態,可供再次複用。能夠看出正是這套策略保證了數據庫鏈接的有效複用,避免頻繁地創建、釋放鏈接所帶來的系統資源開銷。
鏈接池的關閉
當應用程序退出時,應關閉鏈接池,此時應把在鏈接池創建時向數據庫申請的鏈接對象統一歸還給數據庫(即關閉全部數據庫鏈接),這與鏈接池的創建正好是一個相反過程。
鏈接池的配置
數據庫鏈接池中到底要放置多少個鏈接,才能使系統的性能更佳,用minConn和maxConn來限制。minConn是當應用啓動的時候鏈接池所建立的鏈接數,若是過大啓動將變慢,可是啓動後響應更快;若是太小啓動加快,可是最初使用的用戶將由於鏈接池中沒有足夠的鏈接不可避免的延緩了執行速度。所以應該在開發的過程當中設定較小minConn,而在實際應用的中設定較大minConn。maxConn是鏈接池中的最大鏈接數,能夠經過反覆試驗來肯定此飽和點。爲此在鏈接池類ConnectionPool中加入兩個方法getActiveSize()和getOpenSize(),ActiveSize 表示某一時間有多少鏈接正被使用,OpenSize表示鏈接池中有多少鏈接被打開,反映了鏈接池使用的峯值。將這兩個值在日誌信息中反應出來, minConn的值應該小於平均ActiveSize,而maxConn的值應該在activeSize和OpenSize之間。
鏈接池的關鍵技術
事務處理
前面討論的是關於使用數據庫鏈接進行普通的數據庫訪問。對於事務處理,狀況就變得比較複雜。由於事務自己要求原則性的保證,此時就要求對於數據庫的操做符合"All-or-Nothing"原則,即要麼所有完成,要麼什麼都不作。若是簡單的採用上述的鏈接複用的策略,就會發生問題,由於沒有辦法控制屬於同一個事務的多個數據庫操做方法的動做,可能這些數據庫操做是在多個鏈接上進行的,而且這些鏈接可能被其餘非事務方法複用。Connection自己具備提供了對於事務的支持,能夠經過設置Connection的AutoCommit屬性爲false,顯式的調用 commit或rollback方法來實現。可是要安全、高效的進行鏈接複用,就必須提供相應的事務支持機制。方法是:採用顯式的事務支撐方法,每個事務獨佔一個鏈接。這種方法能夠大大下降對於事務處理的複雜性,而且又不會妨礙鏈接的複用。
鏈接管理服務提供了顯式的事務開始、結束(commit或rollback)聲明,以及一個事務註冊表,用於登記事務發起者和事務使用的鏈接的對應關係,經過該表,使用事務的部分和鏈接管理部分就隔離開,由於該表是在運行時根據實際的調用狀況動態生成的。事務使用的鏈接在該事務運行中不能被複用。在實現中,用戶標識是經過使用者所在的線程來標識的。後面的全部對於數據庫的訪問都是經過查找該註冊表,使用已經分配的鏈接來完成的。當事務結束時,從註冊表中刪除相應表項。
併發
爲了使鏈接管理服務有更大的通用性,咱們必需要考慮到多線程環境,即併發問題。在一個多線程的環境下,必需要保證鏈接管理自身數據的一致性和鏈接內部數據的一致性,在這方面Java提供很好的支持(synchronized關鍵字),這樣就很容易使鏈接管理成爲線程安全的。
多數據庫服務器
在實際應用中,應用程序經常須要訪問多個不一樣的數據庫。如何經過同一個鏈接池訪問不一樣的數據庫,是應用程序須要解決的一個核心問題。下面介紹一種解決的途徑:
首先,定義一個數據庫鏈接池參數的類,定義了數據庫的JDBC驅動程序類名,鏈接的URL以及用戶名口令等等一些信息,該類是用於初始化鏈接池的參數:
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綁定的鏈接池對象刪除
}
鏈接池應用的實現
一個完整的鏈接池應用包括三個部分:DBConnectionPool類,負責從鏈接池獲取(或建立)鏈接、將鏈接返回給鏈接池、系統關閉時關閉全部鏈接釋放全部資源;DBConnectionManager類,負責裝載和註冊JDBC驅動、根據屬性文件中定義的屬性建立DBConnectionPool、跟蹤應用程序對鏈接池的引用等;應用程序對鏈接池的使用。
本文實現的數據庫鏈接池包括一個管理類DBConnectionManager,負責提供與多個鏈接池對象(DBConnectionPool類)之間的接口。每個鏈接池對象管理一組封裝過的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; }
摘自百度,方便學習使用。