1. 程式人生 > >資料庫 連線池的深入理解

資料庫 連線池的深入理解

一、應用程式直接獲取資料庫連線的缺點

  使用者每次請求都需要向資料庫獲得連結,而資料庫建立連線通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,資料庫伺服器就需要建立10萬次連線,極大的浪費資料庫的資源,並且極易造成資料庫伺服器記憶體溢位、拓機。如下圖所示:

  

二、使用資料庫連線池優化程式效能

2.1、資料庫連線池的基本概念

資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現的尤為突出.對資料庫連線的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響到程式的效能指標.資料庫連線池正式針對這個問題提出來的.資料庫連線池負責分配,管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是重新建立一個

。如下圖所示:

  

       資料庫連線池在初始化時將建立一定數量的資料庫連線放到連線池中, 這些資料庫連線的數量是由最小資料庫連線數來設定的.無論這些資料庫連線是否被使用,連線池都將一直保證至少擁有這麼多的連線數量.連線池的最大資料庫連線數量限定了這個連線池能佔有的最大連線數,當應用程式向連線池請求的連線數超過最大連線數量時,這些請求將被加入到等待佇列中.

       資料庫連線池的最小連線數和最大連線數的設定要考慮到以下幾個因素:

  1. 最小連線數:是連線池一直保持的資料庫連線,所以如果應用程式對資料庫連線的使用量不大,將會有大量的資料庫連線資源被浪費.
  2. 最大連線數:是連線池能申請的最大連線數,如果資料庫連線請求超過次數,後面的資料庫連線請求將被加入到等待佇列中,這會影響以後的資料庫操作
  3. 如果最小連線數與最大連線數相差很大:那麼最先連線請求將會獲利,之後超過最小連線數量的連線請求等價於建立一個新的資料庫連線.不過,這些大於最小連線數的資料庫連線在使用完不會馬上被釋放,他將被放到連線池中等待重複使用或是空間超時後被釋放.

2.2、編寫資料庫連線池

  編寫連線池需實現java.sql.DataSource介面。DataSource介面中定義了兩個過載的getConnection方法:

  • Connection getConnection()
  • Connection getConnection(String username, String password)

  實現DataSource介面,並實現連線池功能的步驟:

  1. 在DataSource建構函式中批量建立與資料庫的連線,並把建立的連線加入LinkedList物件中。
  2. 實現getConnection方法,讓getConnection方法每次呼叫時,從LinkedList中取一個Connection返回給使用者。
  3. 當用戶使用完Connection,呼叫Connection.close()方法時,Collection物件應保證將自己返回到LinkedList中,而不要把conn還給資料庫。Collection保證將自己返回到LinkedList中是此處程式設計的難點

 資料庫連線池核心程式碼

  使用動態代理技術構建連線池中的connection

複製程式碼
 1 proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
 2             .getClassLoader(), conn.getClass().getInterfaces(),
 3             new InvocationHandler() {
 4         //此處為內部類,當close方法被呼叫時將conn還回池中,其它方法直接執行
 5             public Object invoke(Object proxy, Method method,
 6                       Object[] args) throws Throwable {
 7                 if (method.getName().equals("close")) {
 8                     pool.addLast(conn);
 9                     return null;
10             }
11             return method.invoke(conn, args);
12         }
13     });
複製程式碼

資料庫連線池編寫範例:

複製程式碼
  1 package me.gacl.demo;
  2 
  3 import java.io.InputStream;
  4 import java.io.PrintWriter;
  5 import java.lang.reflect.InvocationHandler;
  6 import java.lang.reflect.Method;
  7 import java.lang.reflect.Proxy;
  8 import java.sql.Connection;
  9 import java.sql.DriverManager;
 10 import java.sql.SQLException;
 11 import java.util.LinkedList;
 12 import java.util.Properties;
 13 import javax.sql.DataSource;
 14 
 15 /**
 16 * @ClassName: JdbcPool
 17 * @Description:編寫資料庫連線池
 18 * @author: 孤傲蒼狼
 19 * @date: 2014-9-30 下午11:07:23
 20 *
 21 */ 
 22 public class JdbcPool implements DataSource{
 23 
 24     /**
 25     * @Field: listConnections
 26     *         使用LinkedList集合來存放資料庫連結,
 27     *        由於要頻繁讀寫List集合,所以這裡使用LinkedList儲存資料庫連線比較合適
 28     */ 
 29     private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
 30     
 31     static{
 32         //在靜態程式碼塊中載入db.properties資料庫配置檔案
 33         InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
 34         Properties prop = new Properties();
 35         try {
 36             prop.load(in);
 37             String driver = prop.getProperty("driver");
 38             String url = prop.getProperty("url");
 39             String username = prop.getProperty("username");
 40             String password = prop.getProperty("password");
 41             //資料庫連線池的初始化連線數大小
 42             int jdbcPoolInitSize =Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
 43             //載入資料庫驅動
 44             Class.forName(driver);
 45             for (int i = 0; i < jdbcPoolInitSize; i++) {
 46                 Connection conn = DriverManager.getConnection(url, username, password);
 47                 System.out.println("獲取到了連結" + conn);
 48                 //將獲取到的資料庫連線加入到listConnections集合中,listConnections集合此時就是一個存放了資料庫連線的連線池
 49                 listConnections.add(conn);
 50             }
 51             
 52         } catch (Exception e) {
 53             throw new ExceptionInInitializerError(e);
 54         }
 55     }
 56     
 57     @Override
 58     public PrintWriter getLogWriter() throws SQLException {
 59         // TODO Auto-generated method stub
 60         return null;
 61     }
 62 
 63     @Override
 64     public void setLogWriter(PrintWriter out) throws SQLException {
 65         // TODO Auto-generated method stub
 66         
 67     }
 68 
 69     @Override
 70     public void setLoginTimeout(int seconds) throws SQLException {
 71         // TODO Auto-generated method stub
 72         
 73     }
 74 
 75     @Override
 76     public int getLoginTimeout() throws SQLException {
 77         // TODO Auto-generated method stub
 78         return 0;
 79     }
 80 
 81     @Override
 82     public <T> T unwrap(Class<T> iface) throws SQLException {
 83         // TODO Auto-generated method stub
 84         return null;
 85     }
 86 
 87     @Override
 88     public boolean isWrapperFor(Class<?> iface) throws SQLException {
 89         // TODO Auto-generated method stub
 90         return false;
 91     }
 92 
 93     /* 獲取資料庫連線
 94      * @see javax.sql.DataSource#getConnection()
 95      */
 96     @Override
 97     public Connection getConnection() throws SQLException {
 98         //如果資料庫連線池中的連線物件的個數大於0
 99         if (listConnections.size()>0) {
100             //從listConnections集合中獲取一個數據庫連線
101             final Connection conn = listConnections.removeFirst();
102             System.out.println("listConnections資料庫連線池大小是" + listConnections.size());
103             //返回Connection物件的代理物件
104             return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
105                 @Override
106                 public Object invoke(Object proxy, Method method, Object[] args)
107                         throws Throwable {
108                     if(!method.getName().equals("close")){
109                         return method.invoke(conn, args);
110                     }else{
111                         //如果呼叫的是Connection物件的close方法,就把conn還給資料庫連線池
112                         listConnections.add(conn);
113                         System.out.println(conn + "被還給listConnections資料庫連線池了!!");
114                         System.out.println("listConnections資料庫連線池大小為" + listConnections.size());
115                         return null;
116                     }
117                 }
118             });
119         }else {
120             throw new RuntimeException("對不起,資料庫忙");
121         }
122     }
123 
124     @Override
125     public Connection getConnection(String username, String password)
126             throws SQLException {
127         return null;
128     }
129 }
複製程式碼

 db.properties配置檔案如下:

1 driver=com.mysql.jdbc.Driver
2 url=jdbc:mysql://localhost:3306/jdbcStudy
3 username=root
4 password=XDP
5 
6 jdbcPoolInitSize=10

寫一個JdbcUtil測試資料庫連線池

複製程式碼
 1 package me.gacl.utils;
 2 
 3 import java.sql.Connection;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7 import me.gacl.demo.JdbcPool;
 8 
 9 public class JdbcUtil {
10     
11     /**
12     * @Field: pool
13     *          資料庫連線池
14     */ 
15     private static JdbcPool pool = new JdbcPool();
16     
17     /**
18     * @Method: getConnection
19     * @Description: 從資料庫連線池中獲取資料庫連線物件
20     * @Anthor:孤傲蒼狼
21     * @return Connection資料庫連線物件
22     * @throws SQLException
23     */ 
24     public static Connection getConnection() throws SQLException{
25         return pool.getConnection();
26     }
27     
28     /**
29     * @Method: release
30     * @Description: 釋放資源,
31     * 釋放的資源包括Connection資料庫連線物件,負責執行SQL命令的Statement物件,儲存查詢結果的ResultSet物件
32     * @Anthor:孤傲蒼狼
33     *
34     * @param conn
35     * @param st
36     * @param rs
37     */ 
38     public static void release(Connection conn,Statement st,ResultSet rs){
39         if(rs!=null){
40             try{
41                 //關閉儲存查詢結果的ResultSet物件
42                 rs.close();
43             }catch (Exception e) {
44                 e.printStackTrace();
45             }
46             rs = null;
47         }
48         if(st!=null){
49             try{
50                 //關閉負責執行SQL命令的Statement物件
51                 st.close();
52             }catch (Exception e) {
53                 e.printStackTrace();
54             }
55         }
56         
57         if(conn!=null){
58             try{
59                 //關閉Connection資料庫連線物件
60                 conn.close();
61             }catch (Exception e) {
62                 e.printStackTrace();
63             }
64         }
65     }
66 }
複製程式碼

 三、開源資料庫連線池

  現在很多WEB伺服器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連線池的實現。通常我們把DataSource的實現,按其英文含義稱之為資料來源,資料來源中都包含了資料庫連線池的實現。
  也有一些開源組織提供了資料來源的獨立實現:

  • DBCP 資料庫連線池
  • C3P0 資料庫連線池

  在使用了資料庫連線池之後,在專案的實際開發中就不需要編寫連線資料庫的程式碼了,直接從資料來源獲得資料庫的連線。

3.1、DBCP資料來源

  DBCP 是 Apache 軟體基金組織下的開源連線池實現,要使用DBCP資料來源,需要應用程式應在系統中增加如下兩個 jar 檔案:

  • Commons-dbcp.jar:連線池的實現
  • Commons-pool.jar:連線池實現的依賴庫

  Tomcat 的連線池正是採用該連線池來實現的。該資料庫連線池既可以與應用伺服器整合使用,也可由應用程式獨立使用。

3.2、在應用程式中加入dbcp連線池

  1.匯入相關jar包
        commons-dbcp-1.2.2.jar、commons-pool.jar
  2、在類目錄下加入dbcp的配置檔案:dbcpconfig.properties

    dbcpconfig.properties的配置資訊如下:

複製程式碼
 1 #連線設定
 2 driverClassName=com.mysql.jdbc.Driver
 3 url=jdbc:mysql://localhost:3306/jdbcstudy
 4 username=root
 5 password=XDP
 6 
 7 #<!-- 初始化連線 -->
 8 initialSize=10
 9 
10 #最大連線數量
11 maxActive=50
12 
13 #<!-- 最大空閒連線 -->
14 maxIdle=20
15 
16 #<!-- 最小空閒連線 -->
17 minIdle=5
18 
19 #<!-- 超時等待時間以毫秒為單位 6000毫秒/1000等於60秒 -->
20 maxWait=60000
21 
22 
23 #JDBC驅動建立連線時附帶的連線屬性屬性的格式必須為這樣:[屬性名=property;] 
24 #注意:"user" 與 "password" 兩個屬性會被明確地傳遞,因此這裡不需要包含他們。
25 connectionProperties=useUnicode=true;characterEncoding=UTF8
26 
27 #指定由連線池所建立的連線的自動提交(auto-commit)狀態。
28 defaultAutoCommit=true
29 
30 #driver default 指定由連線池所建立的連線的只讀(read-only)狀態。
31 #如果沒有設定該值,則“setReadOnly”方法將不被呼叫。(某些驅動並不支援只讀模式,如:Informix)
32 defaultReadOnly=
33 
34 #driver default 指定由連線池所建立的連線的事務級別(TransactionIsolation)。
35 #可用值為下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
36 defaultTransactionIsolation=READ_UNCOMMITTED
複製程式碼

  如下圖所示:

  

  3、在獲取資料庫連線的工具類(如jdbcUtils)的靜態程式碼塊中建立池

複製程式碼
 1 package me.gacl.util;
 2 
 3 import java.io.InputStream;
 4 import java.sql.Connection;
 5 import java.sql.ResultSet;
 6 import java.sql.SQLException;
 7 import java.sql.Statement;

            
           

相關推薦

資料庫連線理解與使用

官方:資料庫連線池(Connection pooling)是程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由程式動態地對池中的連線進行申請,使用,釋放。個人理解:建立資料庫連線是一個很耗時的操作,也容易對資料庫造成安全隱患。所以,在程式初始化的時候,集中建立多個數據庫連線,並把他們集中管理,

資料庫 連線深入理解

一、應用程式直接獲取資料庫連線的缺點   使用者每次請求都需要向資料庫獲得連結,而資料庫建立連線通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,資料庫伺服器就需要建立10萬次連線,極大的浪費資料庫的資源,並且極易造成資料庫伺服器記憶體溢位、

深入理解資料庫連線DBCP

1.DBCP簡介    DBCP(DataBase connection pool),資料庫連線池。是 apache 上的一個 java 連線池專案,也是 tomcat 使用的連線池元件。單獨使用dbcp需要2個包:commons-dbcp.jar,commons-pool.

java jdbc深入理解(connection與threadlocal與資料庫連線和事務實)

1.jdbc連線資料庫,就這樣子Class.forName("com.mysql.jdbc.Driver"); java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);2.通過傳入jdbc url用Drivermanager.getC

理解資料庫連線底層原理之手寫實現

前言 資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。在如今的分散式系統當中,系統的QPS瓶頸往往就

一分鐘理解資料庫連線

問題: 前面介紹了使用JDBC來獲取資料庫的連線,這種方法適用於適用於使用者較少的時候,但當用戶連線量較大時,資源消耗問題就凸顯而出;當一個網站有20萬個使用者訪問時,使用者的每次請求都會向資料庫獲取連線,那麼資料庫伺服器就要建立20萬次連線,而資料庫建立連線時會消耗較大的資源,建立的時間也相對

資料庫連線理解和對比

資料庫連線池的實現及原理:https://www.cnblogs.com/wym789/p/6374440.html 使用jdbc-pool替代dbcp、c3p0等連線池的原因: https://blog.csdn.net/u011267070/article/details/5218593

關於資料庫連線的簡單理解

資料庫連線池更多的是一個本地的概念。以前一直覺得,資料庫連線池是資料庫伺服器上的一個概念,在資料庫伺服器上有一個池,裡邊存放著很多的執行緒的資料庫連線。 最近在分析的時候,發現這是不正確的,或者說,這樣理解是不全面的。其實,連線池更多是一個執行C#程式碼的那個本地機器上的

理解資料庫連線底層之手寫實現

前言 資料庫連線池的基本思想是:為資料庫連線建立一個“緩衝池”,預先在池中放入一定數量的資料庫連線管道,需要時,從池子中取出管道進行使用,操作完畢後,在將管道放入池子中,從而避免了頻繁的向資料庫申請資源,釋放資源帶來的效能損耗。在如今的分散式系統當中,系統的QPS瓶頸往往就

Java資料庫連線細節探討

我們知道,資料庫連線池可以把資料庫的連線快取起來,下次使用的話可以直接取到快取起來的資料庫連線。那麼,在這個過程中有幾個細節需要注意: 1、資料庫的連線數有沒有限制? 2、資料庫會不會自動斷開已經建立的連線? 3、如果資料庫重啟了,但應用沒有重啟,那麼資料庫連線池中的所有連線都不可用了,

JavaWeb_day10_資料庫連線_c3p0_DBUtils

1.在實際開發中,“獲取連線”和“釋放資源”是很消耗系統資源的兩個過程,為了解決此問題,採用連線池技術,共享連線connection 2.連線池概念 3.Java的資料庫連線池的公共介面:javax.sql.DataSource 常見的連線池:DBCP   C3

手寫資料庫連線附gp連線jar包地址

  手寫資料庫連線並,測試.   最近資料庫要連線GP資料庫(GreenplumSQL),在建立連線的時候需要做建立不同的連線數量. 其實當想到寫資料庫連線時,完全可以通過springdata jpa直接寫介面,這是一種思路. 所以在使用的使用,就寫了個dem

JDBC資料庫連線連線資料庫資料庫操作DAO層設計通用更新及查詢方法(二)

上篇文章主要介紹了通過資料庫連線池連線資料庫,然後設計了對資料庫通用更新和查詢方法,本篇文章主要通過例項介紹上篇文章定義的對資料庫操作的幾個方法的使用:     首先我們先在資料庫建立一個學生資訊表Student欄位如圖: 建立好表將配置檔案的資訊改好然後需要建立一

JDBC資料庫連線連線資料庫資料庫操作DAO層設計通用更新及查詢方法(一)

該篇文章介紹了資料庫連線池獲取資料庫連線以及資料庫操作的基本使用,然後主要提供了java專案案例中dao層的一種設計,利用反射的原理定義了通用的查詢方法可以對應所有的表和例項。文章中的每段程式碼都提供了詳細的註釋及邏輯步驟 首先匯入資料庫連線的所需要的jar包:    

springboot配置預設資料庫連線並解決初始連線未生效問題

目前Spring Boot中預設支援的連線池有dbcp,dbcp2, tomcat, hikari三種連線池。  在springboot1.5之前預設tomcat連線池, 版本org.springframework.boot.autoconfigure.jdbc.Data

JFinal配置資料庫連線外掛和表類對映

配置資料庫連線池外掛,此處以Druid為例,還需要配置資料庫訪問外掛,即ActiveRecord外掛,用於建立資料庫中Table和Java Bean的mapping對映: public void configPlugin(Plugins me) { // 配置 druid

druid 資料庫連線的詳細配置

首先說一下自己程式中遇到的問題,前一段時間新寫了一個專案,主要架構改進,為前端提供介面(spring +springmvc+mybatis) 在新專案中使用的是阿里的druid連線池,配置簡單,除了資料庫地址,驅動類,使用者名稱和密碼其他一起都是預設,開始的時候由於專案更新上線頻率比較多,沒有出現太

資料庫連線-資料來源配置

常用的c3p0,DBPC,Druid三大連線池 DBPC <!-- 配置dbcp資料來源 --> <bean id="dataSource2" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSourc

Java配置資料庫連線

dbcp資料庫連線池 DbcpUtils.java import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; impor

python單例與資料庫連線

 單例:專業用來處理連線多的問題(比如連線redis,zookeeper等),全域性只有一個物件   單例程式碼def singleton(cls): instances = {} def _singleton(*args, **kwargs)