1. 程式人生 > >連線池的作用詳解

連線池的作用詳解

連線池的作用       

       連線池的作用:連線池是將已經建立好的連線儲存在池中,當有請求來時,直接使用已經建立好的連線對資料庫進行訪問。這樣省略了建立連線和銷燬連線的過程。這樣效能上得到了提高。

基本原理

(1)建立資料庫連線池物件(伺服器啟動)。
(2)按照事先指定的引數建立初始數量的資料庫連線(即:空閒連線數)。
(3)對於一個數據庫訪問請求,直接從連線池中得到一個連線。如果資料庫連線池物件中沒有空閒的連線,且連線數沒有達到最大(即:最大活躍連線數),建立一個新的資料庫連線。
(4)存取資料庫。
(5)關閉資料庫,釋放所有資料庫連線(此時的關閉資料庫連線,並非真正關閉,而是將其放入空閒佇列中。如實際空閒連線數大於初始空閒連線數則釋放連線

)。
(6)釋放資料庫連線池物件(伺服器停止、維護期間,釋放資料庫連線池物件,並釋放所有連線)。

連線池的概念和為什麼要使用連線池

       連線池是建立和管理一個連線的緩衝池的技術,這些連線準備好被任何需要它們的執行緒使用。連線池放了N個Connection物件,本質上放在記憶體當中,在記憶體中劃出一塊快取物件,應用程式每次從池裡獲得Connection物件,而不是直接從資料裡獲得,這樣不佔用伺服器的記憶體資源。

       這種連線“彙集”起來的技術基於這樣的一個事實:對於大多數應用程式,當它們正在處理通常需要數毫秒

完成的事務時,僅需要能夠訪問JDBC連線的 1 個執行緒。當不處理事務時,這個連線就會閒置。相反,連線池允許閒置的連線被其它需要的執行緒使用。

       事實上,當一個執行緒需要用 JDBC 對一個 GBase 或其它資料庫操作時,它從池中請求一個連線。當這個執行緒使用完了這個連線,將它返回到連線池中,這樣這就可以被其它想使用它的執行緒使用。

       當連線從池中“借出”,它被請求它的執行緒專有地使用。從程式設計的角度來看,這和使用者的執行緒每當需要一個 JDBC 連線的時候呼叫DriverManager.getConnection() 是一樣的,採用

連線池技術,可通過使用新的或已有的連線結束執行緒。

       連線池可以極大的改善使用者的 Java 應用程式的效能,同時減少全部資源的使用。

連線池主要的優點有:

減少連線建立時間

雖然與其它資料庫相比 GBase 提供了較為快速連線功能,但是建立新的 JDBC 連線仍會招致網路和 JDBC 驅動的開銷。如果這類連線是“迴圈”使用的,使用該方式這些花銷就可避免。

簡化的程式設計模式

當使用連線池時,每一個單獨的執行緒能夠像建立了一個自己的 JDBC 連線一樣操作,允許使用者直接使用JDBC程式設計技術

受控的資源使用

如果使用者不使用連線池,而是每當執行緒需要時建立一個新的連線,那麼使用者的應用程式的資源使用會產生非常大的浪費並且可能會導致高負載下的異常發生

注意,每個連到 GBase 的連線在客戶端和伺服器端都有花銷(記憶體,CPU,上下文切換等等)。每個連線均會對應用程式和 GBase 伺服器的可用資源帶來一定的限制。不管這些連線是否在做有用的工作,仍將使用這些資源中的相當一部分。

連線池能夠使效能最大化,同時還能將資源利用控制在一定的水平之下,如果超過該水平,應用程式將崩潰而不僅僅是變慢。

如果不使用連線池會出現的情況

a.佔用伺服器的記憶體資源
b.導致伺服器的速度非常慢

應用連線池的三種方式

a.自定義連線池
b.使用第三方連線池
c.使用伺服器自帶的連線池

       連線池一般比直接連線更有優越性,因為它提高了效能的同時還儲存了寶貴的資源。在整個應用程式的使用過程,當中重複的開啟直接連線將導致效能的下降。而池連線只在伺服器啟動時開啟一次,從而消除了這種效能問題。
        連線池主要考慮的是效能,每次獲取連線和釋放連線都有很大的工作量,會對效能有很大影響;而對資源來說起的是反作用,因為儲存一定數量的連線是要消耗記憶體的。應用程式每次從池裡獲得Connection物件,而不是直接從資料裡獲得,這樣不佔用伺服器的記憶體資源。所以一般要建立連線池,而連線的數量要適當,不能太大,太大會過多消耗資源。(所以,考慮2個方面,一個是記憶體,另一個是資源)。

       連線池就是為了避免重複多次的開啟資料庫連線而造成的效能的下降和系統資源的浪費。

接下來,我們來學習三種連線池的配置:dbcp、c3p0、druid

1、dbcp連線池

連線配置檔案

#資料庫驅動
driverClassName=com.mysql.jdbc.Driver
#資料庫連線地址
url=jdbc:mysql://localhost/ctg
#使用者名稱
username=root
#密碼
password=123456

#連線池的最大資料庫連線數。設為0表示無限制
maxActive=30
#最大空閒數,資料庫連線的最大空閒時間。超過空閒時間,資料庫連
#接將被標記為不可用,然後被釋放。設為0表示無限制
maxIdle=10
#最大建立連線等待時間。如果超過此時間將接到異常。設為-1表示無限制
maxWait=1000

#超過removeAbandonedTimeout時間後,是否進 行沒用連線(廢棄)的回收(預設為false,調整為true) 
removeAbandoned=true
#超過時間限制,回收沒有用(廢棄)的連線(預設為 300秒,調整為180)
removeAbandonedTimeout=180


連線池管理類

package cn.songxinqiang.samples.commonsdbcp.util;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory;

/**
 * tomcat資料庫連線池管理類<br>
 * 使用為tomcat部署環境<br>
 * 需要在類路徑下準備資料庫連線配置檔案dbcp.properties
 * 
 * @author 宋信強
 * @mail [email protected]
 * 
 * @time 2013-12-27
 * 
 */
public class DBManager {
 private static final Log log = LogFactory.getLog(DBManager.class);
 private static final String configFile = "dbcp.properties";

 private static DataSource dataSource;

 static {
  Properties dbProperties = new Properties();
  try {
   dbProperties.load(DBManager.class.getClassLoader()
     .getResourceAsStream(configFile));
   dataSource = BasicDataSourceFactory.createDataSource(dbProperties);

   Connection conn = getConn();
   DatabaseMetaData mdm = conn.getMetaData();
   log.info("Connected to " + mdm.getDatabaseProductName() + " "
     + mdm.getDatabaseProductVersion());
   if (conn != null) {
    conn.close();
   }
  } catch (Exception e) {
   log.error("初始化連線池失敗:" + e);
  }
 }

 private DBManager() {
 }

 /**
  * 獲取連結,用完後記得關閉
  * 
  * @see {@link DBManager#closeConn(Connection)}
  * @return
  */
 public static final Connection getConn() {
  Connection conn = null;
  try {
   conn = dataSource.getConnection();
  } catch (SQLException e) {
   log.error("獲取資料庫連線失敗:" + e);
  }
  return conn;
 }

 /**
  * 關閉連線
  * 
  * @param conn
  *            需要關閉的連線
  */
 public static void closeConn(Connection conn) {
  try {
   if (conn != null && !conn.isClosed()) {
    conn.setAutoCommit(true);
    conn.close();
   }
  } catch (SQLException e) {
   log.error("關閉資料庫連線失敗:" + e);
  }
 }

}

以上是dbcp連線池的配置和使用方式,以上的方式僅供參考。

2、c3p0連線池

資料庫連線配置

c3p0.jdbcUrl=jdbc:oracle:thin:@127.0.0.1:1521:orcl c3p0.driverClass=oracle.jdbc.driver.OracleDriver c3p0.user=test c3p0.password=test

c3p0.acquireIncrement=3  c3p0.idleConnectionTestPeriod=60  c3p0.initialPoolSize=10  c3p0.maxIdleTime=60  c3p0.maxPoolSize=20  c3p0.maxStatements=100  c3p0.minPoolSize=5 

C3P0獲取連線公共類

public class MyC3P0Utils{
	private static DataSource ds;

	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	static {
		ds = new ComboPooledDataSource();//直接使用即可,不用顯示的配置,其會自動識別配置檔案
	}

	public static DataSource getDataSource() {
		return ds;
	}

	public static Connection getConnection() {
		try {
//			 得到當前執行緒上繫結的連線
			Connection conn = tl.get();
			if (conn == null) { // 代表執行緒上沒有繫結連線
				conn = ds.getConnection();
				tl.set(conn);
			}
			return conn;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void startTransaction() {
		try {
			// 得到當前執行緒上繫結連線開啟事務
			Connection conn=getConnection();
			conn.setAutoCommit(false);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void commitTransaction() {
		try {
			Connection conn = tl.get();
			if (conn != null) {
				conn.commit();
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void closeConnection() {
		try {
			Connection conn = tl.get();
			if (conn != null) {
				conn.close();
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			tl.remove(); // 千萬注意,解除當前執行緒上繫結的連結(從threadlocal容器中移除對應當前執行緒的連結)
		}
	}
}

另外提醒一下:不要忘記匯入jar包

匯入C3P0相關jar包

上面就是c3p0連線池的配置方式。

當然連線池的配置方式是不同的,上面配置的方式都是將配置放入到property的檔案中,還有一種就是使用XML檔案的方式,這裡不介紹了。

下面來講解到druid連線池的配置方法

配置檔案

driverClassName=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.97.51:1521:lc8
username=admin8
password=adminpwd8
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200

測試連線:

package taobao_druid;

import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

/**  
* The Class DataSourceUtil.  
*/ 
public class DataSourceUtil {

    /** 使用配置檔案構建Druid資料來源. */     
    public static final int DRUID_MYSQL_SOURCE = 0;

    /** 使用配置檔案構建Druid資料來源. */     
    public static final int DRUID_MYSQL_SOURCE2 = 1;

    /** 使用配置檔案構建Dbcp資料來源. */     
    public static final int DBCP_SOURCE = 4;     
    public static String confile = "druid.properties";     
    public static Properties p = null;

    static {         
        p = new Properties();         
        InputStream inputStream = null;         
        try {             
            //java應用             
            confile = DataSourceUtil.class.getClassLoader().getResource("").getPath()                     + confile;             
            System.out.println(confile);             
            File file = new File(confile);             
            inputStream = new BufferedInputStream(new FileInputStream(file));             
            p.load(inputStream);         
        } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 if (inputStream != null) {
                     inputStream.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }

    /**
      * 根據型別獲取資料來源
      *
      * @param sourceType
      *            資料來源型別
      * @return druid或者dbcp資料來源
      * @throws Exception
      *             the exception
      */
     public static final DataSource getDataSource(int sourceType) throws Exception {         DataSource dataSource = null;
         switch (sourceType) {
         case DRUID_MYSQL_SOURCE:
             dataSource = DruidDataSourceFactory.createDataSource(p);
             break;
         case DRUID_MYSQL_SOURCE2:
             dataSource = DruidDataSourceFactory.createDataSource(p);
             break;
         case DBCP_SOURCE:
             // dataSource = BasicDataSourceFactory.createDataSource(
             // MySqlConfigProperty.getInstance().getProperties()); 
            break;
         }
         return dataSource;
     }
 }