java資料庫連線池實現原理
原文:http://blog.csdn.net/frightingforambition/article/details/25464129
一、為什麼在連線資料庫時要使用連線池
資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現得尤為突出。 一個數據庫連線物件均對應一個物理資料庫連線,每次操作都開啟一個物理連線,使用完都關閉連線,這樣造成系統的 效能低下。 資料庫連線池的解決方案是在應用程式啟動時建立足夠的資料庫連線,並講這些連線組成一個連線池(簡單說:在一個“池”裡放了好多半成品的資料庫聯接物件),由應用程式動態地對池中的連線進行申請、使用和釋放。對於多於連線池中連線數的併發請求,應該在請求佇列中排隊等待。並且應用程式可以根據池中連線的使用率,動態增加或減少池中的連線數。
二、資料庫連線池的基本原理
資料庫連線池的基本思想就是為資料庫連線 建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。我們可以通過設定 連線池最大連線數來防止系統無盡的與資料庫連線。更為重要的是我們可以通過連線池的管理機制監視資料庫的連線的數量?使用情況,為系統開發?測試及效能調 整提供依據。
三、資料庫連線池的工作原理
連線池的工作原理主要由三部分組成,分別為連線池的建立、連線池中連線的使用管理、連線池的關閉。
第一、連線池的建立。一般在系統初始化時,連線池會根據系統配置建立,並在池中建立了幾個連線物件,以便使用時能從連線池中獲取。連線池中的連線不能隨意建立和關閉,這樣避免了連線隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連線池,例如Vector、Stack等。
第二、連線池的管理。連線池管理策略是連線池機制的核心,連線池內連線的分配和釋放對系統的效能有很大的影響。其管理策略是:
當客戶請求資料庫連線時,首先檢視連線池中是否有空閒連線,如果存在空閒連線,則將連線分配給客戶使用;如果沒有空閒連線,則檢視當前所開的連線數是否已經達到最大連線數,如果沒達到就重新建立一個連線給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則丟擲異常給客戶。
當客戶釋放資料庫連線時,先判斷該連線的引用次數是否超過了規定值,如果超過就從連線池中刪除該連線,否則保留為其他客戶服務。
該策略保證了資料庫連線的有效複用,避免頻繁的建立、釋放連線所帶來的系統資源開銷。
第三、連線池的關閉。當應用程式退出時,關閉連線池中所有的連線,釋放連線池相關的資源,該過程正好與建立相反。
四、連線池關鍵問題分析
1、併發問題
為了使連線管理服務具有最大的通用性,必須考慮多執行緒環境,即併發問題。這個問題相對比較好解決,因為Java語言自身提供了對併發管理的支 持,使用synchronized關鍵字即可確保執行緒是同步的。使用方法為直接在類方法前面加上synchronized關鍵字,如:
public synchronized Connection getConnection()
2、多資料庫伺服器和多使用者
對於大型的企業級應用,常常需要同時連線不同的資料庫(如連線Oracle和Sybase)。如何連線不同的資料庫呢?我們採用的策略是:設計 一個符合單例模式的連線池管理類,在連線池管理類的唯一例項被建立時讀取一個資原始檔,其中資原始檔中存放著多個數據庫的url地址()?使用者名稱()?密 碼()等資訊。如 tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根據資原始檔提 供的資訊,建立多個連線池類的例項,每一個例項都是一個特定資料庫的連線池。連線池管理類例項為每個連線池例項取一個名字,通過不同的名字來管理不同的連 接池。
對於同一個資料庫有多個使用者使用不同的名稱和密碼訪問的情況,也可以通過資原始檔處理,即在資原始檔中設定多個具有相同url地址,但具有不同使用者名稱和密碼的資料庫連線資訊。
3、事務處理
我們知道,事務具有原子性,此時要求對資料庫的操作符合“ALL-ALL-NOTHING”原則,即對於一組SQL語句要麼全做,要麼全不做。
在Java語言中,Connection類本身提供了對事務的支援,可以通過設定Connection的AutoCommit屬性為 false,然後顯式的呼叫commit或rollback方法來實現。但要高效的進行Connection複用,就必須提供相應的事務支援機制。可採用 每一個事務獨佔一個連線來實現,這種方法可以大大降低事務管理的複雜性。
4、連線池的分配與釋放
連線池的分配與釋放,對系統的效能有很大的影響。合理的分配與釋放,可以提高連線的複用度,從而降低建立新連線的開銷,同時還可以加快使用者的訪問速度。
對於連線的管理可使用空閒池。即把已經建立但尚未分配出去的連線按建立時間存放到一個空閒池中。每當使用者請求一個連線時,系統首先檢查空閒池內 有沒有空閒連線。如果有就把建立時間最長(通過容器的順序存放實現)的那個連線分配給他(實際是先做連線是否有效的判斷,如果可用就分配給使用者,如不可用 就把這個連線從空閒池刪掉,重新檢測空閒池是否還有連線);如果沒有則檢查當前所開連線池是否達到連線池所允許的最大連線數(maxConn),如果沒有 達到,就新建一個連線,如果已經達到,就等待一定的時間(timeout)。如果在等待的時間內有連線被釋放出來就可以把這個連線分配給等待的使用者,如果 等待時間超過預定時間timeout,則返回空值(null)。系統對已經分配出去正在使用的連線只做計數,當使用完後再返還給空閒池。對於空閒連線的狀 態,可開闢專門的執行緒定時檢測,這樣會花費一定的系統開銷,但可以保證較快的響應速度。也可採取不開闢專門執行緒,只是在分配前檢測的方法。
5、連線池的配置與維護
連線池中到底應該放置多少連線,才能使系統的效能最佳?系統可採取設定最小連線數(minConn)和最大連線數(maxConn)來控制連線 池中的連線。最小連線數是系統啟動時連線池所建立的連線數。如果建立過多,則系統啟動就慢,但建立後系統的響應速度會很快;如果建立過少,則系統啟動的很 快,響應起來卻慢。這樣,可以在開發時,設定較小的最小連線數,開發起來會快,而在系統實際使用時設定較大的,因為這樣對訪問客戶來說速度會快些。最大連 接數是連線池中允許連線的最大數目,具體設定多少,要看系統的訪問量,可通過反覆測試,找到最佳點。
如何確保連線池中的最小連線數呢?有動態和靜態兩種策略。動態即每隔一定時間就對連線池進行檢測,如果發現連線數量小於最小連線數,則補充相應數量的新連線,以保證連線池的正常運轉。靜態是發現空閒連線不夠時再去檢查。
五、連線池實現程式碼(java)
- package book.util;
- import java.sql.Connection;
- import java.sql.DatabaseMetaData;
- import java.sql.Date;
- import java.sql.Driver;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.Vector;
- publicclass Pool {
- publicstaticvoid main(String[] args) {
- Pool pool = new Pool("com.microsoft.sqlserver.jdbc.SQLServerDriver","jdbc:sqlserver://localhost:1433;DataBaseName=Book","sa","aaaaaa");
- try {
- pool.createConnections(4);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- Connection conn = pool.getConnection();
- try {
- String sql = "select * from allbook";
- PreparedStatement ps;
- ps = conn.prepareStatement(sql);
- ResultSet rs=ps.executeQuery();
- while(rs.next()){
- System.out.println(rs.getString("BOOKNAME"));
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }finally{
- pool.returnConnection(conn);
- }
- //long startTime=System.currentTimeMillis();
- //long endTime=System.currentTimeMillis();
- //System.out.println("程式執行時間: "+(endTime-startTime)+"ms");
- }
- private String jdbcDriver = "";//資料庫驅動
- private String dbUrl = "";//資料庫url
- private String dbUsername = "";//資料庫使用者名稱
- private String dbPassword = "";//資料庫密碼
- private String testTable = "";
- privateint initialConnectionsNum = 10;//連線池初始連線數
- privateint maxConnectionsNum = 50;//連線池最大連線數
- privateint incrementalConnections = 5;//每次動態新增的連線數
- private Vector<PooledConnection> connections = null;//向量,存放連線池中的連線,初始為空
- /*無參建構函式*/
- public Pool()
- {}
- /*帶引數的建構函式
- * 初始化資料庫驅動、資料庫url、資料庫使用者名稱、資料庫密碼、測試表
- * */
- public Pool(String driver, String url, String name, String pass)
- {
- this.jdbcDriver = driver;
- this.dbUrl = url;
- this.dbUsername = name;
- this.dbPassword = pass;
- //this.testTable = table;
- try {
- this.createPool();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /*函式,建立連線池*/
- publicsynchronizedvoid createPool()
- throws InstantiationE