1. 程式人生 > >JDBC的資料庫連線池的工作原理

JDBC的資料庫連線池的工作原理

  隨著資訊科技的高速發展與廣泛應用,資料庫技術在資訊科技領域中的位置越來越重要,尤其是網路應用和電子商務的迅速發展,都需要資料庫技術支援動態Web站點的執行,而傳統的開發模式是:首先在主程式(如Servlet、Beans)中建立資料庫連線;然後進行SQL操作,對資料庫中的物件進行查詢、修改和刪除等操作;最後斷開資料庫連線。使用這種開發模式,對於一個簡單的資料庫應用,由於資料庫的訪問不是很頻繁,只需要在訪問資料庫時建立一個連線,用完後就關閉它,這樣做不會明顯的增大系統的開銷。但是對於一個複雜的資料庫應用,情況就完全不同:頻繁的建立、關閉資料庫,會極大的降低系統的效能,增大系統的開銷,甚至成為系統的瓶頸。另外使用這種傳統的模式,還必須管理資料庫的每一個連線,以確保他們能正確關閉,如果出現程式異常而導致某些連線未能關閉,將引起資料庫系統中的記憶體洩露,最終不得不重啟資料庫。因此採用執行速度更快、資料庫訪問效率更高的資料庫技術,以提高系統的執行效率將是至關重要的。 

    為了解決這一問題,在JDBC2.0中提出了JDBC連線池技術,通過在客戶之間共享一組連線,而不是在它們需要的時候再為它們生成,這樣就可以改善資源使用,提高應用程式的響應能力。 

    JDBC 概述 

    JDBC(Java Database Connectivity,Java資料庫連線)是一種用於執行SQL語句的JavaAPI,可以為多種關係型資料庫(如Oracle、Sybase、SQL Server、Access等)提供統一訪問介面,它由一組Java語言編寫的類和介面組成,使資料庫開發人員能夠用標準JavaAPI編寫資料庫應用程式。 

    連線池技術 

    1、連線池原理 

    連線池技術的核心思想是:連線複用,通過建立一個數據庫連線池以及一套連線使用、分配、管理策略,使得該連線池中的連線可以得到高效、安全的複用,避免了資料庫連線頻繁建立、關閉的開銷。另外,由於對JDBC中的原始連線進行了封裝,從而方便了資料庫應用對於連線的使用(特別是對於事務處理),提高了開發效率,也正是因為這個封裝層的存在,隔離了應用的本身的處理邏輯和具體資料庫訪問邏輯,使應用本身的複用成為可能。連線池主要由三部分組成:連線池的建立、連線池中連線的使用管理、連線池的關閉。下面就著重討論這三部分及連線池的配置問題。 

    1.1 連線池的建立 

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

    1.2 連線池的管理 

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

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

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

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

    1.3 連線池的關閉 

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

    1.4 連線池的配置 

    資料庫連線池中到底要放置多少個連線,才能使系統的效能更佳,用minConn和maxConn來限制。minConn是當應用啟動的時候連線池所建立的連線數,如果過大啟動將變慢,但是啟動後響應更快;如果過小啟動加快,但是最初使用的使用者將因為連線池中沒有足夠的連線不可避免的延緩了執行速度。因此應該在開發的過程中設定較小minConn,而在實際應用的中設定較大minConn。maxConn是連線池中的最大連線數,可以通過反覆試驗來確定此飽和點。為此在連線池類ConnectionPool中加入兩個方法getActiveSize()和getOpenSize(),ActiveSize 表示某一時間有多少連線正被使用,OpenSize表示連線池中有多少連線被開啟,反映了連線池使用的峰值。將這兩個值在日誌資訊中反應出來, minConn的值應該小於平均ActiveSize,而maxConn的值應該在activeSize和OpenSize之間。 

    2、連線池的關鍵技術 

    2.1 事務處理 

    前面討論的是關於使用資料庫連線進行普通的資料庫訪問。對於事務處理,情況就變得比較複雜。因為事務本身要求原則性的保證,此時就要求對於資料庫的操作符合"All-All-Nothing"原則,即要麼全部完成,要麼什麼都不做。如果簡單的採用上述的連線複用的策略,就會發生問題,因為沒有辦法控制屬於同一個事務的多個數據庫操作方法的動作,可能這些資料庫操作是在多個連線上進行的,並且這些連線可能被其他非事務方法複用。Connection本身具有提供了對於事務的支援,可以通過設定Connection的AutoCommit屬性為false,顯式的呼叫 commit或rollback方法來實現。但是要安全、高效的進行連線複用,就必須提供相應的事務支援機制。方法是:採用顯式的事務支撐方法,每一個事務獨佔一個連線。這種方法可以大大降低對於事務處理的複雜性,並且又不會妨礙連線的複用。 

    連線管理服務提供了顯式的事務開始、結束(commit或rollback)宣告,以及一個事務登錄檔,用於登記事務發起者和事務使用的連線的對應關係,通過該表,使用事務的部分和連線管理部分就隔離開,因為該表是在執行時根據實際的呼叫情況動態生成的。事務使用的連線在該事務執行中不能被複用。在實現中,使用者標識是通過使用者所在的執行緒來標識的。後面的所有對於資料庫的訪問都是通過查詢該登錄檔,使用已經分配的連線來完成的。當事務結束時,從登錄檔中刪除相應表項。 

    2.2 封裝 

    從上面的論述可以看出,普通的資料庫方法和事務方法對於連線的使用(分配、釋放)是不同的,為了便於使用,對外提供一致的操作介面,我們對連線進行了封裝:普通連線和事務連線,並利用了Java中的強大的面向物件特性:多型。普通連線和事務連線均實現了一個DbConnection介面,對於介面中定義的方法,分別根據自己的特點作了不同的實現,這樣在對於連線的處理上就非常的一致了。 

    2.3 併發 

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

    2.4 多資料庫伺服器 

    在實際應用中,應用程式常常需要訪問多個不同的資料庫。如何通過同一個連線池訪問不同的資料庫,是應用程式需要解決的一個核心問題。下面介紹一種解決的途徑: 

    首先,定義一個數據庫連線池引數的類,定義了資料庫的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(String name, 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; 
 tr 
 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; } 
    結束語 

    當前Web應用程式廣泛採用B/S結構,其併發性決定了多使用者同時訪問資料庫的問題。本文闡述的基於JDBC的資料庫連線池技術已成功應用於基於Web的高職教學系統開發中,並建立了資料庫連線池例項來說明和證實連線池的訪問方法。只有充分運用連線池訪問技術,才能提高資料庫的訪問效率,改善Web應用,從而減少系統開銷,大大提高整個Web應用系統的執行效率。