數據庫鏈接池技術詳解

前言

今天來說一下數據庫鏈接池技術.其實這個名詞也就是聽起來高大上一點,實際上並非很複雜的內容,相信在個人講解下,而且本身實際的將代碼寫一遍以後,可以對這項技術有較爲深入的理解.廢話很少說,開始講解.java

數據庫鏈接池技術概述

所謂的數據庫鏈接池技術,就是用來分配,管理,釋放數據庫鏈接的.你也許會問,好像我直接用JDBC也可以實現這些功能吧. 嗯,你說的沒錯,JDBC確實也能夠,可是,你記不記得,咱們使用JDBC技術的時候,每次用完了,是否是都會將鏈接關閉;等到下一次再用的時候,是否是都得將數據庫鏈接再打開? 實際上,數據庫連接資源是十分寶貴的,咱們在小型的項目中還看不出來,在高併發的項目中,你會發現,這樣頻繁的打開和關閉數據庫連接是對服務器的一種摧殘,十分影響效率. 那麼,數據庫鏈接池是如何作的呢? 實現思路是這樣的:在每次有訪問的時候,數據庫鏈接池會給用戶分配一個數據庫鏈接,當用戶用完了鏈接以後,鏈接池再將鏈接回收,放回一個鏈接集合中. 原理就是這樣的,咱們來看一下這張圖加深印象 mysql

這裏寫圖片描述
這樣你可能仍是不太清楚,並且,數據庫鏈接池要考慮的東西要比上面說的更復雜,不過不要懼怕,我經過實際的代碼來幫你理解一下. 備註:下面的代碼是本身實現一個簡單的數據庫鏈接池,着重瞭解數據庫鏈接池的原理.

本身實現一個數據庫鏈接池

注意:下面的代碼我是分塊講解的,你一個個粘貼下來,最後確定也是能運行的,爲了方便,我會在講完以後,給出完整的實現代碼.sql

  • 定義初始化鏈接數目,最大鏈接數以及當前已經鏈接的數目 一開始,當數據庫鏈接池啓動的時候,爲了實現上面的需求,咱們確定是要先給出幾個已經完成的鏈接的,這樣用戶訪問的時候就能直接拿到了;此外,當某一段時間的訪問用戶超過我定義的鏈接池中的鏈接個數,確定是要額外新建鏈接給用戶使用;固然,這個新建的鏈接確定是不能無限制的,不然仍是會很影響效率的,因此,咱們還要定義最大的鏈接數.
private final int init_count = 3; //初始化連接數目
private final int max_count = 6; //最大鏈接數
private int current_count = 0; //到當前鏈接數
複製代碼
  • 那麼,咱們一開始新建的鏈接池要放在哪裏供用戶使用呢?確定是要建立一個鏈接集合的,這樣的操做比較方便.至於爲何要使用LinkedList這樣一個集合,一會就會介紹的.
private LinkedList<Connection> pool = new LinkedList<Connection>();
複製代碼
  • 剛纔說了,一開始咱們確定是要初始化鏈接給用戶使用的,那麼,就須要在鏈接池啓動的時候就新建必定數量的連接.分析後發現,放在構造函數中初始化連接是最好不過的了,最後再將鏈接放在連接集合中.
//構造函數,初始化連接放入鏈接池
 public MyPool() {
     for (int i=0;i<init_count;i++){
         //記錄當前鏈接數
         current_count++;
         //createConnection是自定義的建立連接函數.
         Connection connection = createConnection();
         pool.addLast(connection);
     }
 }
複製代碼
  • 建立一個新的鏈接,這個就沒啥好說的了,畢竟你要的連接都是從這來的.
public Connection createConnection() {
	Class.forName("com.mysql.jdbc.Driver");
	Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/keyan","root","root");
	return connection;
}
複製代碼
  • 獲取連接 當用戶來訪問的時候,咱們確定是要給用戶一個鏈接的,若是池中沒有鏈接了(全部鏈接均被佔用),那麼就要建立新的鏈接,使用createConnection()函數,固然,這個鏈接的個數確定是不能超過最大鏈接數的.若是不知足這兩個條件,那麼直接拋出異常.
public Connection getConnection() {
     if (pool.size() > 0){
         //removeFirst刪除第一個而且返回
         //如今你必定看懂了我說的爲什要用LinkedList了吧,由於下面的這個
         //removeFirst()方法會將集合中的第一個元素刪除,可是還會返回第一個元素
         //這樣就省去了咱們不少沒必要要的麻煩
         return pool.removeFirst();
     }
     if (current_count < max_count){
         //記錄當前使用的鏈接數
         current_count++;
         //建立連接
         return createConnection();
     }
     throw new RuntimeException("當前連接已經達到最大鏈接數");
}
複製代碼
  • 釋放資源 當用戶使用完了鏈接以後,咱們要作的並非關閉鏈接,而是將鏈接從新放入資源池LinkedList之中,這樣就省去了一遍又一遍的鏈接關閉.這個就是鏈接池的核心內容.是否是很簡單?
public void releaseConnection(Connection connection){
    if (pool.size() < init_count){
        pool.addLast(connection);
        current_count--;
    }else {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
複製代碼

整個的實現過程就是這樣的,下面我把所有的代碼貼出來,方便你們學習.數據庫

//單元測試
@Test
public class MyPool {
    private final int init_count = 3; //初始化連接數目
    private final int max_count = 6; //最大
    private int current_count = 0; //到當前鏈接數
    //鏈接池,用來存放初始化連接
    private LinkedList<Connection> pool = new LinkedList<Connection>();

    //構造函數,初始化連接放入鏈接池
    public MyPool() {
        for (int i=0;i<init_count;i++){
            //記錄當前鏈接數
            current_count++;
            Connection connection = createConnection();
            pool.addLast(connection);
        }
    }

    //建立新的鏈接
    public Connection createConnection() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/keyan","root","root");
            return connection;
        }catch (Exception e){
            System.out.println("數據庫連接異常");
            throw new RuntimeException();
        }
    }

    //獲取連接
    public Connection getConnection() {
        if (pool.size() > 0){
            //removeFirst刪除第一個而且返回
            return pool.removeFirst();
        }
        if (current_count < max_count){
            //記錄當前使用的鏈接數
            current_count++;
            //建立連接
            return createConnection();
        }
        throw new RuntimeException("當前連接已經達到最大鏈接數");
    }

    //釋放連接
    public void releaseConnection(Connection connection){
        if (pool.size() < init_count){
            pool.addLast(connection);
            current_count--;
        }else {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
複製代碼

本身跑了一遍代碼以後,你是否是發現原來看起來很複雜的技術,並不像咱們想得那樣? 好了,介紹完了基本的數據庫鏈接池技術原理以後,咱們就要介紹兩個開源的優秀的數據鏈接池技術. 其實,真正的數據庫鏈接池要考慮的東西比咱們剛纔寫的這玩意複雜,現階段不須要咱們寫這樣複雜的東西,不過若是你感興趣的話,能夠看看數據庫鏈接池的源代碼--沒錯,這兩個鏈接池都是開源的.OK,接下來就開始吧!tomcat

優秀的數據庫鏈接池

首先,sun公司規定,鏈接池技術需實現javax.sql.DataSource接口,也就是說,若是你要本身實現數據庫鏈接池,那麼就必須實現這個接口.是否是很牛比的樣子,實際上,做爲標準制定方,sun公司仍是有不少要求的,這是做爲標準制定者的權利,因此,企業或者國家每每會對一些關鍵技術標準的制定打得頭破血流.額,扯遠了...bash

DBPC

其實,你可能不知道,若是你的tomcat通過特殊的配置,也是能夠做爲數據庫鏈接池使用的,由於tomcat內置的就是DBPC.. 想要使用DBPC,你必須導入三個jar文件:commons-dbcp2-2.2.0.jar,commons-pool2-2.5.0.jar,commons-logging-1.2.jar.我想,以你的聰明才智,想要獲取這三個jar文件,必定是小菜一疊,這是一個軟件開發者的必備技能--學會如何搜索. 使用起來就很是簡單了.使用的方式主要有兩種,一種是硬編碼的方式,就是本身手動設置各類參數,另一種就是配置相應的配置文件,而後載入就好了.服務器

硬編碼實現DBPC

BasicDataSource dataSource = new BasicDataSource();
//參數配置:初始化鏈接數,最大鏈接數,鏈接字符串,驅動,用戶,密碼
dataSource.setInitialSize(3);   //最大初始化連接
dataSource.setMaxTotal(6);      //最大連接
dataSource.setMaxIdle(3000);    //最大空閒時間
dataSource.setUrl("jdbc:mysql:///keyan");   //url
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");

//獲取連接
Connection connection = dataSource.getConnection();
connection.prepareStatement("SELECT * FROM e_person").execute();
connection.close();
複製代碼

使用查詢的時候,只須要按照原來JDBC的操做方式就好了,這個沒啥好說的,按照我上面的配置方式實現就好了,必定不要忘記導入包.微信

配置文件方式實現

//建立properties配置文件
Properties properties = new Properties();
//獲取文件流
InputStream in = DBCPTest.class.getResourceAsStream("db.properties");
//加載配置文件
properties.load(in);
//建立數據源對象
BasicDataSource dataSource = BasicDataSourceFactory.createDataSource(properties);

//獲取連接
Connection connection = dataSource.getConnection();
ResultSet resultSet = connection.prepareStatement("SELECT * FROM e_person").executeQuery();
while (resultSet.next()){
   System.out.println(resultSet.getString("work_name"));
}
connection.close();
複製代碼

上面是基本的操做流程,下面咱們看一下這個xml文件的配置 文件db.properties併發

url=jdbc:mysql:///keyan
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
initialSize=3
maxActive=6
maxIdle=3000
複製代碼

特別要注意的一點是,這個配置文件必定要放在和你的這DBPC類放在同一個包下. 都完成了以後,直接用就能夠了.函數

C3P0

c3p0一樣是很是優秀的鏈接池技術,這個須要導入的文件比較少,只有一個c3p0-0.9.1.2.jar.本身去網站上找一下就行了. 使用c3p0一樣是有兩種方式,分別也是硬編碼方式和配置文件方式.

硬編碼方式

//配置相關的參數
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql:///keyan");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setInitialPoolSize(3);
dataSource.setMaxPoolSize(6);
dataSource.setMaxIdleTime(1000);

Connection connection = dataSource.getConnection();
//sql語句
ResultSet resultSet = connection.prepareStatement("SELECT * FROM e_project").executeQuery();
while (resultSet.next()){
   System.out.println(resultSet.getString("project_name"));
}
connection.close();
複製代碼

和上面同樣,簡單的使用一下就好了,知道如何使用就行.至於更加深層次的東西,有興趣的話慢慢研究吧.

配置文件方式

ComboPooledDataSource dataSource = new ComboPooledDataSource();
Connection connection = dataSource.getConnection();
ResultSet resultSet = connection.prepareStatement("SELECT * FROM e_project").executeQuery();
while (resultSet.next()){
    System.out.println(resultSet.getString("project_name"));
}
connection.close();
複製代碼

乍一看,你可能會問,不是配置文件方式實現嗎?沒錯,只是這個是隱式的調用,不須要你實現,只須要新建一個ComboPooledDataSource對象就好了,默認調用xml文件,文件路徑是src根目錄.也就是說,你只須要將配置文件寫在src目錄下就好了.重點是如何寫這個xml文件. 注意,文件名c3p0-config.xml,這個文件名不能改,必須是這個,文件路徑必須是src根目錄,不然讀取不到.

<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </default-config>
</c3p0-config>
複製代碼

配置文件的參數大致上用到的就這麼多,固然,你也能夠去找一下相關的資料,瞭解一下更加詳細的參數意義,這些參數,我麼平常使用足夠了.

補充內容:數據庫鏈接池的優勢

資源重用

因爲數據庫鏈接得以重用,避免了頻繁建立,釋放鏈接引發的大量性能開銷。在減小系統消耗的基礎上,另外一方面也增長了系統運行環境的平穩性。

更快的系統反應速度

數據庫鏈接池在初始化過程當中,每每已經建立了若干數據庫鏈接置於鏈接池中備用。此時鏈接的初始化工做均已完成。對於業務請求處理而言,直接利用現有可用鏈接,避免了數據庫鏈接初始化和釋放過程的時間開銷,從而減小了系統的響應時間

新的資源分配手段

對於多應用共享同一數據庫的系統而言,可在應用層經過數據庫鏈接池的配置,實現某一應用最大可用數據庫鏈接數的限制,避免某一應用獨佔全部的數據庫資源

統一的鏈接管理,避免數據庫鏈接泄露:

在較爲完善的數據庫鏈接池實現中,可根據預先的佔用超時設定,強制回收被佔用鏈接,從而避免了常規數據庫鏈接操做中可能出現的資源泄露

結語

感謝您的閱讀,歡迎指正博客中存在的問題,也能夠跟我聯繫,一塊兒進步,一塊兒交流!

微信公衆號:進擊的程序狗 郵箱:roobtyan@outlook.com 我的博客:roobtyan.cn 掃描下面的二維碼關注我吧,你將收穫到意想不到的東西喲…… 給你們準備了一份很是棒的JAVA的視頻教程,從JAVA基礎一直到JAVAWEB,還有很是強大的項目實戰。 就在個人微信公衆號裏,回覆java就可查看,免費的呦!

這裏寫圖片描述
相關文章
相關標籤/搜索