1、爲何在鏈接數據庫時要使用鏈接池
數據庫鏈接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤其突出。 一個數據庫鏈接對象均對應一個物理數據庫鏈接,每次操做都打開一個物理鏈接,使用完都關閉鏈接,這樣形成系統的 性能低下。 數據庫鏈接池的解決方案是在應用程序啓動時創建足夠的數據庫鏈接,並講這些鏈接組成一個鏈接池(簡單說:在一個「池」裏放了好多半成品的數據庫聯接對象),由應用程序動態地對池中的鏈接進行申請、使用和釋放。對於多於鏈接池中鏈接數的併發請求,應該在請求隊列中排隊等待。而且應用程序能夠根據池中鏈接的使用率,動態增長或減小池中的鏈接數。 鏈接池技術儘量多地重用了消耗內存地資源,大大節省了內存,提升了服務器地服務效率,可以支持更多的客戶服務。經過使用鏈接池,將大大提升程序運行效率,同時,咱們能夠經過其自身的管理機制來監視數據庫鏈接的數量、使用狀況等。 java
2、數據庫鏈接池的基本原理
數據庫鏈接池的基本思想就是爲數據庫鏈接 創建一個「緩衝池」。預先在緩衝池中放入必定數量的鏈接,當須要創建數據庫鏈接時,只需從「緩衝池」中取出一個,使用完畢以後再放回去。咱們能夠經過設定 鏈接池最大鏈接數來防止系統無盡的與數據庫鏈接。更爲重要的是咱們能夠經過鏈接池的管理機制監視數據庫的鏈接的數量?使用狀況,爲系統開發?測試及性能調 整提供依據。sql
3、數據庫鏈接池的工做原理
鏈接池的工做原理主要由三部分組成,分別爲鏈接池的創建、鏈接池中鏈接的使用管理、鏈接池的關閉。數據庫
第1、鏈接池的創建。通常在系統初始化時,鏈接池會根據系統配置創建,並在池中建立了幾個鏈接對象,以便使用時能從鏈接池中獲取。鏈接池中的鏈接不能隨意建立和關閉,這樣避免了鏈接隨意創建和關閉形成的系統開銷。Java中提供了不少容器類能夠方便的構建鏈接池,例如Vector、Stack等。服務器
第2、鏈接池的管理。鏈接池管理策略是鏈接池機制的核心,鏈接池內鏈接的分配和釋放對系統的性能有很大的影響。其管理策略是:多線程
當客戶請求數據庫鏈接時,首先查看鏈接池中是否有空閒鏈接,若是存在空閒鏈接,則將鏈接分配給客戶使用;若是沒有空閒鏈接,則查看當前所開的鏈接數是否已經達到最大鏈接數,若是沒達到就從新建立一個鏈接給請求的客戶;若是達到就按設定的最大等待時間進行等待,若是超出最大等待時間,則拋出異常給客戶。併發
當客戶釋放數據庫鏈接時,先判斷該鏈接的引用次數是否超過了規定值,若是超過就從鏈接池中刪除該鏈接,不然保留爲其餘客戶服務。app
該策略保證了數據庫鏈接的有效複用,避免頻繁的創建、釋放鏈接所帶來的系統資源開銷。函數
第3、鏈接池的關閉。當應用程序退出時,關閉鏈接池中全部的鏈接,釋放鏈接池相關的資源,該過程正好與建立相反。sqlserver
4、鏈接池關鍵問題分析
一、併發問題性能
爲了使鏈接管理服務具備最大的通用性,必須考慮多線程環境,即併發問題。這個問題相對比較好解決,由於Java語言自身提供了對併發管理的支 持,使用synchronized關鍵字便可確保線程是同步的。使用方法爲直接在類方法前面加上synchronized關鍵字,如:
public synchronized Connection getConnection()
二、多數據庫服務器和多用戶
對於大型的企業級應用,經常須要同時鏈接不一樣的數據庫(如鏈接Oracle和Sybase)。如何鏈接不一樣的數據庫呢?咱們採用的策略是:設計 一個符合單例模式的鏈接池管理類,在鏈接池管理類的惟一實例被建立時讀取一個資源文件,其中資源文件中存放着多個數據庫的url地址()?用戶名()?密 碼()等信息。如 tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根據資源文件提 供的信息,建立多個鏈接池類的實例,每個實例都是一個特定數據庫的鏈接池。鏈接池管理類實例爲每一個鏈接池實例取一個名字,經過不一樣的名字來管理不一樣的連 接池。
對於同一個數據庫有多個用戶使用不一樣的名稱和密碼訪問的狀況,也能夠經過資源文件處理,即在資源文件中設置多個具備相同url地址,但具備不一樣用戶名和密碼的數據庫鏈接信息。
三、事務處理
咱們知道,事務具備原子性,此時要求對數據庫的操做符合「ALL-ALL-NOTHING」原則,即對於一組SQL語句要麼全作,要麼全不作。
在Java語言中,Connection類自己提供了對事務的支持,能夠經過設置Connection的AutoCommit屬性爲 false,而後顯式的調用commit或rollback方法來實現。但要高效的進行Connection複用,就必須提供相應的事務支持機制。可採用 每個事務獨佔一個鏈接來實現,這種方法能夠大大下降事務管理的複雜性。
四、鏈接池的分配與釋放
鏈接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,能夠提升鏈接的複用度,從而下降創建新鏈接的開銷,同時還能夠加快用戶的訪問速度。
對於鏈接的管理可以使用空閒池。即把已經建立但還沒有分配出去的鏈接按建立時間存放到一個空閒池中。每當用戶請求一個鏈接時,系統首先檢查空閒池內 有沒有空閒鏈接。若是有就把創建時間最長(經過容器的順序存放實現)的那個鏈接分配給他(實際是先作鏈接是否有效的判斷,若是可用就分配給用戶,如不可用 就把這個鏈接從空閒池刪掉,從新檢測空閒池是否還有鏈接);若是沒有則檢查當前所開鏈接池是否達到鏈接池所容許的最大鏈接數(maxConn),若是沒有 達到,就新建一個鏈接,若是已經達到,就等待必定的時間(timeout)。若是在等待的時間內有鏈接被釋放出來就能夠把這個鏈接分配給等待的用戶,若是 等待時間超過預約時間timeout,則返回空值(null)。系統對已經分配出去正在使用的鏈接只作計數,當使用完後再返還給空閒池。對於空閒鏈接的狀 態,可開闢專門的線程定時檢測,這樣會花費必定的系統開銷,但能夠保證較快的響應速度。也可採起不開闢專門線程,只是在分配前檢測的方法。
五、鏈接池的配置與維護
鏈接池中到底應該放置多少鏈接,才能使系統的性能最佳?系統可採起設置最小鏈接數(minConn)和最大鏈接數(maxConn)來控制鏈接 池中的鏈接。最小鏈接數是系統啓動時鏈接池所建立的鏈接數。若是建立過多,則系統啓動就慢,但建立後系統的響應速度會很快;若是建立過少,則系統啓動的很 快,響應起來卻慢。這樣,能夠在開發時,設置較小的最小鏈接數,開發起來會快,而在系統實際使用時設置較大的,由於這樣對訪問客戶來講速度會快些。最大連 接數是鏈接池中容許鏈接的最大數目,具體設置多少,要看系統的訪問量,可經過反覆測試,找到最佳點。
如何確保鏈接池中的最小鏈接數呢?有動態和靜態兩種策略。動態即每隔必定時間就對鏈接池進行檢測,若是發現鏈接數量小於最小鏈接數,則補充相應數量的新鏈接,以保證鏈接池的正常運轉。靜態是發現空閒鏈接不夠時再去檢查。
5、鏈接池實現代碼(java)
[java] view plain copy
- 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;
- public class Pool {
- public static void 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 = "";
- private int initialConnectionsNum = 10;//鏈接池初始鏈接數
- private int maxConnectionsNum = 50;//鏈接池最大鏈接數
- private int 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();
- }
- }
-
- /*函數,建立鏈接池*/
- public synchronized void createPool()
- throws InstantiationException, IllegalAccessException,
- ClassNotFoundException, SQLException
- {
- /*確保鏈接池爲建立,若是已經建立,則保存鏈接的向量不爲空
- * */
- if (this.connections != null)
- {
- return ;
- }
- //驅動器實例化
- Driver driver = (Driver)(Class.forName(this.jdbcDriver).newInstance());
- //註冊驅動器
- DriverManager.registerDriver(driver);
- //建立保存鏈接的向量
- this.connections = new Vector<PooledConnection>();
- //建立數據庫鏈接
- this.createConnections(this.initialConnectionsNum);
- }
-
- /*函數,建立數據庫鏈接
- * */
- private void createConnections (int num) throws SQLException
- {
- /*循環建立鏈接
- * 須要首先檢查當前鏈接數是否已經超出鏈接池最大鏈接數
- * */
- for (int i = 0; i < num; ++i)
- {
- //檢查
- if (this.connections.size() >= this.maxConnectionsNum)
- {
- return;
- }
- //建立鏈接
- this.connections.addElement
- (new PooledConnection(newConnection()));
- }
-
- }
-
- /*函數,建立一個數據庫鏈接*/
- private Connection newConnection() throws SQLException
- {
- /*建立鏈接*/
- Connection con = DriverManager.getConnection(this.dbUrl,
- this.dbUsername, this.dbPassword);
- /*若是是第一次建立鏈接,則檢查所鏈接的數據庫的容許最大鏈接數是否小於
- * 咱們所設定的最大鏈接數*/
- if (this.connections.size() == 0)
- {
- DatabaseMetaData metadata = con.getMetaData();
- //獲得數據庫最大鏈接數
- int dbMaxConnectionsNum = metadata.getMaxConnections();
- //若是數據庫最大鏈接數更小,則更改咱們所設定的鏈接池最大鏈接數
- if (dbMaxConnectionsNum > 0
- && this.maxConnectionsNum > dbMaxConnectionsNum)
- {
- this.maxConnectionsNum = dbMaxConnectionsNum;
- }
- }
- return con;
- }
-
- /*函數,獲得一個可用鏈接
- * */
- public synchronized Connection getConnection ()
- {
- Connection con = null;
- /*檢查鏈接池是否已經創建*/
- if (this.connections == null)
- {
- return con;
- }
- //獲得一個可用鏈接
- try {
- con = this.getFreeConnection();
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- //若是未找到合適鏈接,循環等待、查找,知道找到合適鏈接
- while(con == null)
- {
- this.wait(30);
- try {
- con = this.getFreeConnection();
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- return con;
- }
-
-
- /*函數,獲得一個可用鏈接*/
- private Connection getFreeConnection() throws SQLException
- {
- Connection con = null;
- //查找一個可用鏈接
- con = this.findFreeConnection();
- //若是未找到可用鏈接,就創建一些新的鏈接,再次查找
- if (con == null)
- {
- this.createConnections(this.incrementalConnections);
- //再次查找
- con = this.findFreeConnection();
- }
- return con;
- }
-
-
- /*函數,從現有鏈接中查找一個可用鏈接
- * 在現有的鏈接中(向量connections中)找到一個空閒鏈接,
- * 並測試這個連接是否可用,若不可用則從新創建鏈接,替換原來的鏈接*/
- private Connection findFreeConnection () throws SQLException
- {
- Connection con = null;
- for (int i = 0; i < this.connections.size(); ++i)
- {
- PooledConnection pol = (PooledConnection)this.connections.get(i);
- if (!pol.isBusy())
- {
- /*若是此連接未被使用,則返回這個鏈接並,設置正在使用標誌*/
- con = pol.getCon();
- pol.setBusy(true);
- /*測試鏈接是否可用*/
- if (!this.testCon(con))
- {
- con = this.newConnection();
- pol.setCon(con);
- }
- break;
- }
- }
- return con;
- }
-
- /*函數,測試鏈接是否可用
- * */
- private boolean testCon (Connection con)
- {
- boolean useable = true;
- try
- {
- Statement st = con.createStatement();
- ResultSet rs = st.executeQuery("select count(*) from " + this.testTable);
- rs.next();
- }
- catch(SQLException e)
- {
- /*上面拋出異常,鏈接不可用,關閉*/
- useable = false;
- this.closeConnection(con);
- }
- return useable;
- }
-
- /*函數,將使用完畢的鏈接放回鏈接池中
- * */
- public void returnConnection(Connection con)
- {
- /*確保鏈接池存在*/
- if (this.connections == null)
- {
- return ;
- }
- for (int i = 0; i < this.connections.size(); ++i)
- {
- PooledConnection pool = this.connections.get(i);
- //找到相應鏈接,設置正在使用標誌爲false
- if (con == pool.getCon())
- {
- pool.setBusy(false);
- }
- }
-
- }
-
- /*函數,刷新鏈接池中的鏈接*/
- public synchronized void refreshConneciontPool () throws SQLException
- {
- /*確保鏈接池存在*/
- if (this.connections == null)
- {
- return ;
- }
- for (int i = 0; i < this.connections.size(); ++i)
- {
- PooledConnection pool = this.connections.get(i);
- if (pool.isBusy())
- {
- this.wait(5000);
- }
- this.closeConnection(pool.getCon());
- pool.setCon(this.newConnection());
- pool.setBusy(false);
- }
- }
-
- /*函數,關閉鏈接池*/
- public void closeConnectionPool()
- {
- /*確保鏈接池存在*/
- if (this.connections == null)
- {
- return ;
- }
- for (int i = 0; i < this.connections.size(); ++i)
- {
- PooledConnection pool = this.connections.get(i);
- if (pool.isBusy())
- {
- this.wait(5000);
- }
- this.closeConnection(pool.getCon());
- this.connections.remove(i);
- }
- this.connections = null;
- }
-
- /*函數,暫時無可用鏈接,進入等待隊列等待m秒,再試
- * */
- private void wait(int mSecond)
- {
- try {
- Thread.sleep(mSecond);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * @return the jdbcDriver
- */
- public String getJdbcDriver() {
- return jdbcDriver;
- }
-
- /**
- * @param jdbcDriver the jdbcDriver to set
- */
- public void setJdbcDriver(String jdbcDriver) {
- this.jdbcDriver = jdbcDriver;
- }
-
- /**
- * @return the dbUrl
- */
- public String getDbUrl() {
- return dbUrl;
- }
-
- /**
- * @param dbUrl the dbUrl to set
- */
- public void setDbUrl(String dbUrl) {
- this.dbUrl = dbUrl;
- }
-
- /**
- * @return the dbUsername
- */
- public String getDbUsername() {
- return dbUsername;
- }
-
- /**
- * @param dbUsername the dbUsername to set
- */
- public void setDbUsername(String dbUsername) {
- this.dbUsername = dbUsername;
- }
-
- /**
- * @return the dbPassword
- */
- public String getDbPassword() {
- return dbPassword;
- }
-
- /**
- * @param dbPassword the dbPassword to set
- */
- public void setDbPassword(String dbPassword) {
- this.dbPassword = dbPassword;
- }
-
- /**
- * @return the testTable
- */
- public String getTestTable() {
- return testTable;
- }
-
- /**
- * @param testTable the testTable to set
- */
- public void setTestTable(String testTable) {
- this.testTable = testTable;
- }
-
- /**
- * @return the initialConnectionsNum
- */
- public int getInitialConnectionsNum() {
- return initialConnectionsNum;
- }
-
- /**
- * @param initialConnectionsNum the initialConnectionsNum to set
- */
- public void setInitialConnectionsNum(int initialConnectionsNum) {
- this.initialConnectionsNum = initialConnectionsNum;
- }
-
- /**
- * @return the maxConnectionsNum
- */
- public int getMaxConnectionsNum() {
- return maxConnectionsNum;
- }
-
- /**
- * @param maxConnectionsNum the maxConnectionsNum to set
- */
- public void setMaxConnectionsNum(int maxConnectionsNum) {
- this.maxConnectionsNum = maxConnectionsNum;
- }
-
- /**
- * @return the incrementalConnections
- */
- public int getIncrementalConnections() {
- return incrementalConnections;
- }
-
- /**
- * @param incrementalConnections the incrementalConnections to set
- */
- public void setIncrementalConnections(int incrementalConnections) {
- this.incrementalConnections = incrementalConnections;
- }
-
- /**
- * @return the connections
- */
- public Vector<PooledConnection> getConnections() {
- return connections;
- }
-
- /**
- * @param connections the connections to set
- */
- public void setConnections(Vector<PooledConnection> connections) {
- this.connections = connections;
- }
-
- /*函數,鏈接使用完畢,關閉鏈接*/
- private void closeConnection (Connection con)
- {
- try
- {
- con.close();
- }
- catch(SQLException e)
- {
- e.printStackTrace();
- }
- }
-
-
- /*內部使用的保存數據庫鏈接的類
- * 兩個成員變量:鏈接、是否正在使用*/
- class PooledConnection
- {
- private Connection con = null;//鏈接
- private boolean busy = false;//是否正在使用,默認爲非
-
- /*構造函數*/
- public PooledConnection(Connection con)
- {
- this.con = con;
- }
-
- /**
- * @return the con
- */
- public Connection getCon() {
- return con;
- }
-
- /**
- * @param con the con to set
- */
- public void setCon(Connection con) {
- this.con = con;
- }
-
- /**
- * @return the busy
- */
- public boolean isBusy() {
- return busy;
- }
-
- /**
- * @param busy the busy to set
- */
- public void setBusy(boolean busy) {
- this.busy = busy;
- }
- }
-
- }