歡迎光臨
每天分享高質量文章

資料庫連線池極簡教程

來自:架構師小秘圈(微訊號:seexmq)

一,常規資料庫連線

常規資料庫連線一般由以下六個步驟構成:

  1. 裝載資料庫驅動程式;

  2. 建立資料庫連線;

  3. 建立資料庫操作物件

  4. 訪問資料庫,執行sql陳述句;

  5. 處理傳回結果集

  6. 斷開資料庫連線。

public class TestMysqlConn {
 public static void main(String[] args) {
  Connection con; 
  Statement stmt; 
  ResultSet rs;
    
  try {
   //1,裝載資料庫驅動程式
   Class.forName("com.mysql.jdbc.Driver").newInstance(); 
   //2,建立資料庫連線
   con = DriverManager.getConnection("jdbc:mysql://3xmq.com:3306/test","root","root");
   //3,建立資料庫操作物件
   stmt = con.createStatement(); 
   //4,執行sql陳述句
   rs = stmt.executeQuery("select * from _test"); 
   //5,處理傳回結果集
   while(rs.next()){
   int num = rs.getInt("id");
   String name = rs.getString("name");
   String des = rs.getString("description");
   System.out.println(num + " " + name + " " + des);
  }
  //6,斷開資料庫連線
  stmt.close();
  conn.close();
  } catch (Exception e) {
   e.printStackTrace();
   System.out.println("連線失敗");
  }
 }
}

二,常規資料庫連線底層原理

資料庫本身實際上就是一個Server端程式在跑,我們開發的程式連線資料庫,相當於啟動了一個Client端,連線到Server端,也就是C/S樣式!那麼資料庫連線本質上是基於什麼協議呢?以mysql連線為例,常見兩種連線場景命令如下:

1,mysql -h localhost -uroot -p(本地樣式)

2,mysql -h 127.0.0.1 -uroot -p(IP樣式)

對場景一,使用tcpdump抓包如下:

可以看到並沒有抓到網路請求資料,說明它走的是本地socket協議,unix domain socket。

對場景二,使用tcpdump進行抓包如下:

可以看到,mysql的連線過程,內部實際上是經過tcp/ip協議的,mysql上層基於tcp/ip協議封裝了自己的一套訊息協議!說白了,底層是基於tcp/ip socket 協議!

在mysql中使用命令:show status,可以看到mysql實際上會建立一個執行緒來處理客戶端連線上來的連線!如下圖:

Threads_connected連線數是1,mysql此時有一個連線!

Threads_created為3,說明曾經有3個connection連線過資料庫!

Threads_cached為2,mysql內部的執行緒連線池,將空閑的連線不是立即銷毀而是放到執行緒連線池中,如果新加進來連線不是立刻建立執行緒而是先從執行緒連線池中找到空閑的連線執行緒,然後分配,如果沒有才建立新的執行緒。可見mysql內部已經為我們做優化了。

Threads_running為1,當前活躍執行緒數為1。

小提示:

1,Threads_catched值不是無限大的,一般為32左右。 mysql是可以調整單執行緒和多執行緒樣式的,單執行緒只允許一個執行緒連線mysql,其他連線將會被拒絕。 

2,資料庫本地unix domain socket連線快於tcp/ip連線

三,常規資料庫連線最佳化空間

以mysql為例,要做最佳化,首先要尋找資料庫連線佔用的資源有哪些?

1,mysql每個連線是會建立一個執行緒的,可以登入mysql輸入show status檢視Threads_connected和Threads_created的大小,那麼我們每連線一次mysql就會建立一個執行緒,每次斷開又會銷毀一個執行緒。

資料庫連線的建立和銷毀本質就是執行緒的建立和銷毀,而建立執行緒和銷毀執行緒的資源消耗是非常大的。系統為每個執行緒分配棧空間,可以透過ulimis -s來檢視,ubuntu 14.04預設是8M,那麼100個連線就是800M,很吃記憶體的。其次mysql資料庫會為每個連線分配連線緩衝區和結果緩衝區,也是要消耗時間的。

2,mysql的每次連線,都會進行tcp3次握手和斷開時的4次揮手,分配一些快取空間,也會消耗一定的時間。

如下圖:

資料庫連線池有效的避免了上述的問題,資料庫連線池技術的思想非常簡單,將資料庫連線作為物件儲存在一個Vector物件中,一旦資料庫連線建立後,不同的資料庫訪問請求就可以共享這些連線,這樣,透過復用這些已經建立的資料庫連線,可以剋服上述缺點,極大地節省系統資源和時間。 
也就是我們提前建立好這些連線,然後需要用去取連線即可。和執行緒池的思想是一致的。

四,資料庫連線池

資料庫連線池(Connection pooling)是程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由程式動態地對池中的連線進行申請,使用,釋放。建立資料庫連線是一個很耗時的操作,也容易對資料庫造成安全隱患。所以,在程式初始化的時候,集中建立多個資料庫連線,並把他們集中管理,供程式使用,可以保證較快的資料庫讀寫速度,還更加安全可靠。

連線池基本的思想是在系統初始化的時候,將資料庫連線作為物件儲存在記憶體中,當使用者需要訪問資料庫時,並非建立一個新的連線,而是從連線池中取出一個已建立的空閑連線物件。使用完畢後,使用者也並非將連線關閉,而是將連線放回連線池中,以供下一個請求訪問使用。而連線的建立、斷開都由連線池自身來管理。同時,還可以透過設定連線池的引數來控制連線池中的初始連線數、連線的上下限數以及每個連線的最大使用次數、最大空閑時間等等,也可以透過其自身的管理機制來監視資料庫連線的數量、使用情況等。如下圖:

資料庫連線池機制:

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

資料庫連線池在初始化時,按照連線池最小連線數,建立相應數量連線放入池中,無論是否被使用。當連線請求數大於最大連線數閥值時,會加入到等待佇列!

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

  1. 最小連線數:是連線池一直保持的資料庫連線,所以如果應用程式對資料庫連線的使用量不大,將會有大量的資料庫連線資源被浪費.

  2. 最大連線數:是連線池能申請的最大連線數,如果資料庫連線請求超過次數,後面的資料庫連線請求將被加入到等待佇列中,這會影響以後的資料庫操作

  3. 如果最小連線數與最大連線數相差很大:那麼最先連線請求將會獲利,之後超過最小連線數量的連線請求等價於建立一個新的資料庫連線.不過,這些大於最小連線數的資料庫連線在使用完不會馬上被釋放,他將被放到連線池中等待重覆使用或是空間超時後被釋放.

五,常見資料庫連線池

在Java中開源的常用的資料庫連線池有以下幾種 :

1)DBCP

DBCP是一個依賴Jakarta commons-pool物件池機制的資料庫連線池.DBCP可以直接的在應用程式中使用,Tomcat的資料源使用的就是DBCP。

2)c3p0

c3p0是一個開放原始碼的JDBC連線池,它在lib目錄中與Hibernate一起釋出,包括了實現jdbc3和jdbc2擴充套件規範說明的Connection 和Statement 池的DataSources 物件。

3)Druid

阿裡出品,淘寶和支付寶專用資料庫連線池,但它不僅僅是一個資料庫連線池,它還包含一個ProxyDriver,一系列內建的JDBC元件庫,一個SQL Parser。支援所有JDBC相容的資料庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。

Druid針對Oracle和MySql做了特別最佳化,比如Oracle的PS Cache記憶體佔用最佳化,MySql的ping檢測最佳化。

Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支援,這是一個手寫的高效能SQL Parser,支援Visitor樣式,使得分析SQL的抽象語法樹很方便。

簡單SQL陳述句用時10微秒以內,複雜SQL用時30微秒。

透過Druid提供的SQL Parser可以在JDBC層攔截SQL做相應處理,比如說分庫分表、審計等。Druid防禦SQL註入攻擊的WallFilter就是透過Druid的SQL Parser分析語意實現的。

六,資料庫連線池配置

連線池配置大體可以分為基本配置、關鍵配置、效能配置等主要配置。

6.1 基本配置

基本配置是指連線池進行資料庫連線的四個基本必需配置:

傳遞給JDBC驅動的用於連線資料庫的使用者名稱、密碼、URL以及驅動類名。

DBCP

c3p0

Druid

使用者名稱

username

user

username

密碼

password

password

password

URL

url

jdbcUrl

jdbcUrl

驅動類名

driverClassName

driverClass

driverClassName

註:在Druid連線池的配置中,driverClassName可配可不配,如果不配置會根據url自動識別dbType(資料庫型別),然後選擇相應的driverClassName。

6.2 關鍵配置

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

最小連線數:

是資料庫一直保持的資料庫連線數,所以如果應用程式對資料庫連線的使用量不大,將有大量的資料庫資源被浪費。

初始化連線數:

連線池啟動時建立的初始化資料庫連線數量。

最大連線數

是連線池能申請的最大連線數,如果資料庫連線請求超過此數,後面的資料庫連線請求被加入到等待佇列中。

最大等待時間:

當沒有可用連線時,連線池等待連線被歸還的最大時間,超過時間則丟擲異常,可設定引數為0或者負數使得無限等待(根據不同連線池配置)。

DBCP

c3p0

Druid

最小連線數

minIdle(0)

minPoolSize(3)

minIdle(0)

初始化連線數

initialSize(0)

initialPoolSize(3)

initialSize(0)

最大連線數

maxTotal(8)

maxPoolSize(15)

maxActive(8)

最大等待時間

maxWaitMillis(毫秒)

maxIdleTime(0秒)

maxWait(毫秒)

註1:在DBCP連線池的配置中,還有一個maxIdle的屬性,表示最大空閑連線數,超過的空閑連線將被釋放,預設值為8。對應的該屬性在Druid連線池已不再使用,配置了也沒有效果,c3p0連線池則沒有對應的屬性。

註2:資料庫連線池在初始化的時候會建立initialSize個連線,當有資料庫操作時,會從池中取出一個連線。如果當前池中正在使用的連線數等於maxActive,則會等待一段時間,等待其他操作釋放掉某一個連線,如果這個等待時間超過了maxWait,則會報錯;如果當前正在使用的連線數沒有達到maxActive,則判斷當前是否空閑連線,如果有則直接使用空閑連線,如果沒有則新建立一個連線。在連線使用完畢後,不是將其物理連線關閉,而是將其放入池中等待其他操作復用。

6.3 效能配置

預快取設定:

即是PSCache,PSCache對支援遊標的資料庫效能提升巨大,比如說oracle。JDBC的標準引數,用以控制資料源內載入的PreparedStatements數量。但由於預快取的statements屬於單個connection而不是整個連線池,所以設定這個引數需要考慮到多方面的因素。

單個連線擁有的最大快取數:要啟用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改為true。在Druid中,不會存在Oracle下PSCache佔用記憶體過多的問題,可以把這個數值配置大一些,比如說100

連線有效性檢測設定:

連線池內部有機制判斷,如果當前的總的連線數少於miniIdle,則會建立新的空閑連線,以保證連線數得到miniIdle。如果當前連線池中某個連線在空閑了timeBetweenEvictionRunsMillis時間後任然沒有使用,則被物理性的關閉掉。有些資料庫連線的時候有超時限制(mysql連線在8小時後斷開),或者由於網路中斷等原因,連線池的連線會出現失效的情況,這時候設定一個testWhileIdle引數為true,可以保證連線池內部定時檢測連線的可用性,不可用的連線會被拋棄或者重建,最大情況的保證從連線池中得到的Connection物件是可用的。當然,為了保證絕對的可用性,你也可以使用testOnBorrow為true(即在獲取Connection物件時檢測其可用性),不過這樣會影響效能。

超時連線關閉設定:

removeAbandoned引數,用來檢測到當前使用的連線是否發生了連線洩露,所以在程式碼內部就假定如果一個連線建立連線的時間很長,則將其認定為洩露,繼而強制將其關閉掉。


●本文編號266,以後想閱讀這篇文章直接輸入266即可

●輸入m獲取文章目錄

推薦↓↓↓

 

Linux學習

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂