數據庫鏈接池

1、數據庫鏈接池的原理

基本原理

  • 對於一個簡單的數據庫應用,因爲對於數據庫的訪問不是很頻繁。這時能夠簡單地在須要訪問數據庫時,就新建立一個鏈接,用完後就關閉它,這樣作也不會帶來什麼明顯的性能上的開銷。可是對於一個複雜的數據庫應用,狀況就徹底不一樣了。頻繁的創建、關閉鏈接,會極大的減低系統的性能,由於對於鏈接的使用成了系統性能的瓶頸。
  • 鏈接複用。經過創建一個數據庫鏈接池以及一套鏈接使用管理策略,使得一個數據庫鏈接能夠獲得高效、安全的複用,避免了數據庫鏈接頻繁創建、關閉的開銷。
  • 對於共享資源,有一個很著名的設計模式:資源池。該模式正是爲了解決資源頻繁分配、釋放所形成的問題的。把該模式應用到數據庫鏈接管理領域,就是創建一個數據庫鏈接池,提供一套高效的鏈接分配、使用策略,最終目標是實現鏈接的高效、安全的複用。

數據庫鏈接池的基本原理是在內部對象池中維護必定數量的數據庫鏈接,並對外暴露數據庫鏈接獲取和返回方法。如:css

外部使用者可經過getConnection 方法獲取鏈接,使用完畢後再經過releaseConnection 方法將鏈接返回,注意此時鏈接並無關閉,而是由鏈接池管理器回收,併爲下一次使用作好準備。html

數據庫鏈接池技術帶來的優點:

1. 資源重用

因爲數據庫鏈接獲得重用,避免了頻繁建立、釋放鏈接引發的大量性能開銷。在減小系統消耗的基礎上,另外一方面也增進了系統運行環境的平穩性(減小內存碎片以及數據庫臨時進程/線程的數量)。java

2. 更快的系統響應速度mysql

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

3. 新的資源分配手段web

對於多應用共享同一數據庫的系統而言,可在應用層經過數據庫鏈接的配置,實現數據庫鏈接池技術,幾年錢也許仍是個新鮮話題,對於目前的業務系統而言,若是設計中尚未考慮到鏈接池的應用,那麼…….快在設計文檔中加上這部分的內容吧。某一應用最大可用數據庫鏈接數的限制,避免某一應用獨佔全部數據庫資源。spring

4. 統一的鏈接管理,避免數據庫鏈接泄漏sql

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

2、一個最小化的數據庫鏈接池實現

1.前言

數據庫應用,在許多軟件系統中常常用到,是開發中大型系統不可缺乏的輔助。但若是對數據庫資源沒有很好地管理(如:沒有及時回收數據庫的遊標(ResultSet)、Statement、鏈接 (Connection)等資源),每每會直接致使系統的穩定。這類不穩定因素,不僅僅由數據庫或者系統自己一方引發,只有系統正式使用後,隨着流量、用戶的增長,纔會逐步顯露。
在基於Java開發的系統中,JDBC是程序員和數據庫打交道的主要途徑,提供了完備的數據庫操做方法接口。但考慮到規範的適用性,JDBC只提供了最直接的數據庫操做規範,對數據庫資源管理,如:對物理鏈接的管理及緩衝,指望第三方應用服務器(Application Server)的提供。
本文,以JDBC規範爲基礎,介紹相關的數據庫鏈接池機制,並就若是以簡單的方式,實現有效地管理數據庫資源介紹相關實現技術。設計模式

2.鏈接池技術背景

2.1 JDBC

JDBC是一個規範,遵循JDBC接口規範,各個數據庫廠家各自實現本身的驅動程序(Driver),以下圖所示:

數據庫鏈接池的實現及原理
應用在獲取數據庫鏈接時,須要以URL的方式指定是那種類型的Driver,在得到特定的鏈接後,可按照固定的接口操做不一樣類型的數據庫,如: 分別獲取Statement、執行SQL得到ResultSet等,以下面的例子 :

import java.sql.*;

DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection dbConn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:oracle","username","password");
Statement st = dbConn.createStatement();
ResultSet rs = st.executeQuery("select * from demo_table");

…some data source operation in here

rs.close();
st.close();
dbConn.close();

在完成數據操做後,還必定要關閉全部涉及到的數據庫資源。這雖然對應用程序的邏輯沒有任何影響,可是關鍵的操做。上面是個簡單的例子,若是攙和衆多的if-else、exception,資源的管理也不免百密一疏。如同C中的內存泄漏問題,Java系統也一樣會面臨崩潰的惡運。因此數據庫資源的管理依賴於應用系統自己,是不安全、不穩定的一種隱患。

2.2 JDBC鏈接池

在標準JDBC對應用的接口中,並無提供資源的管理方法。因此,缺省的資源管理由應用本身負責。雖然在JDBC規範中,屢次說起資源的關閉/回收及其餘的合理運用。但最穩妥的方式,仍是爲應用提供有效的管理手段。因此,JDBC爲第三方應用服務器(Application Server)提供了一個由數據庫廠家實現的管理標準接口:鏈接緩衝(connection pooling)。引入了鏈接池( Connection Pool )的概念 ,也就是以緩衝池的機制管理數據庫的資源。

JDBC最經常使用的資源有三類:
— Connection: 數據庫鏈接。
— Statement: 會話聲明。
— ResultSet: 結果集遊標。

分別存在如下的關係 :

clipboard.png

數據庫鏈接池的實現及原理
這是一種「爺—父—子」的關係,對Connection的管理,就是對數據庫資源的管理。舉個例子: 若是想肯定某個數據庫鏈接(Connection)是否超時,則須要肯定其(全部的)子Statement是否超時,一樣,須要肯定全部相關的 ResultSet是否超時;在關閉Connection前,須要關閉全部相關的Statement和ResultSet。
所以,鏈接池(Connection Pool)所起到的做用,不只僅簡單地管理Connection,還涉及到 Statement和ResultSet。

2.3 鏈接池(ConnectionPool)與資源管理

ConnectionPool以緩衝池的機制,在必定數量上限範圍內,控制管理Connection,Statement和ResultSet。任何數據庫的資源是有限的,若是被耗盡,則沒法得到更多的數據服務。
在大多數狀況下,資源的耗盡不是因爲應用的正常負載太高,而是程序緣由。
在實際工做中,數據資源每每是瓶頸資源,不一樣的應用都會訪問同一數據源。其中某個應用耗盡了數據庫資源後,意味其餘的應用也沒法正常運行。所以,ConnectionPool的第一個任務是限制:每一個應用或系統能夠擁有的最大資源。也就是肯定鏈接池的大小(PoolSize)。
ConnectionPool的第二個任務:在鏈接池的大小(PoolSize)範圍內,最大限度地使用資源,縮短數據庫訪問的使用週期。許多數據庫中,鏈接(Connection)並非資源的最小單元,控制Statement資源比Connection更重要。以Oracle爲例:
每申請一個鏈接(Connection)會在物理網絡(如 TCP/IP網絡)上創建一個用於通信的鏈接,在此鏈接上還能夠申請必定數量的Statement。同一鏈接可提供的活躍Statement數量能夠達到幾百。在節約網絡資源的同時,縮短了每次會話週期(物理鏈接的創建是個費時的操做)。但在通常的應用中,多數按照2.1範例操做,這樣有10個程序調用,則會產生10次物理鏈接,每一個Statement單獨佔用一個物理鏈接,這是極大的資源浪費。 ConnectionPool能夠解決這個問題,讓幾10、幾百個Statement只佔用同一個物理鏈接, 發揮數據庫原有的優勢。
經過ConnectionPool對資源的有效管理,應用能夠得到的Statement總數到達 :

(併發物理鏈接數)×(每一個鏈接可提供的Statement數量)

例如某種數據庫可同時創建的物理鏈接數爲 200個,每一個鏈接可同時提供250個Statement,那麼ConnectionPool最終爲應用提供的併發Statement總數爲: 200 × 250 = 50,000個。這是個併發數字,不多有系統會突破這個量級。因此在本節的開始,指出資源的耗盡與應用程序直接管理有關。
對資源的優化管理,很大程度上依靠數據庫自身的JDBC Driver是否具有。有些數據庫的JDBC Driver並不支持Connection與Statement之間的邏輯鏈接功能,如SQLServer,咱們只能等待她自身的更新版本了。
對資源的申請、釋放、回收、共享和同步,這些管理是複雜精密的。因此,ConnectionPool另外一個功能就是,封裝這些操做,爲應用提供簡單的,甚至是不改變應用風格的調用接口。

3.簡單JDBC鏈接池的實現

根據第二章中原理機制,Snap-ConnectionPool(一種簡單快速的鏈接池工具,可在www.snapbug.net下載)按照部分的JDBC規範,實現了鏈接池所具有的對數據庫資源有效管理功能。

3.1 體系描述

在JDBC規範中,應用經過驅動接口(Driver Interface)直接方法數據庫的資源。爲了有效、合理地管理資源,在應用與JDBC Driver之間,增長了鏈接池: Snap-ConnectionPool。而且經過面向對象的機制,使鏈接池的大部分操做是透明的。參見下圖,Snap-ConnectionPool的體系:

clipboard.png

數據庫鏈接池的實現及原理
圖中所示,經過實現JDBC的部分資源對象接口( Connection, Statement, ResultSet ),在 Snap-ConnectionPool內部分別產生三種邏輯資源對象: PooledConnection, PooledStatement和 PooledResultSet。它們也是鏈接池主要的管理操做對象,而且繼承了JDBC中相應的從屬關係。這樣的體系有如下幾個特色:

透明性。在不改變應用原有的使用JDBC驅動接口的前提下,提供資源管理的服務。應用系統,如同原有的 JDBC,使用鏈接池提供的邏輯對象資源。簡化了應用程序的鏈接池改造。
資源封裝。複雜的資源管理被封裝在 Snap-ConnectionPool內部,不須要應用系統過多的干涉。管理操做的可靠性、安全性由鏈接池保證。應用的干涉(如:主動關閉資源),只起到優化系統性能的做用,遺漏操做不會帶來負面影響。
資源合理應用。按照JDBC中資源的從屬關係,Snap-ConnectionPool不只對Connection進行緩衝處理,對Statement也有相應的機制處理。在2.3已描述,合理運用Connection和Statement之間的關係,能夠更大限度地使用資源。因此,Snap- ConnectionPool封裝了Connection資源,經過內部管理PooledConnection,爲應用系統提供更多的Statement 資源。
資源連鎖管理。Snap-ConnectionPool包含的三種邏輯對象,繼承了JDBC中相應對象之間的從屬關係。在內部管理中,也依照從屬關係進行連鎖管理。例如:判斷一個Connection是否超時,須要根據所包含的Statement是否活躍;判斷Statement也要根據 ResultSet的活躍程度。

3.2 鏈接池集中管理ConnectionManager

ConnectionPool是Snap-ConnectionPool的鏈接池對象。在Snap-ConnectionPool內部,能夠指定多個不一樣的鏈接池(ConnectionPool)爲應用服務。ConnectionManager管理全部的鏈接池,每一個鏈接池以不一樣的名稱區別。經過配置文件適應不一樣的數據庫種類。以下圖所示:

clipboard.png

數據庫鏈接池的實現及原理
  經過ConnectionManager,能夠同時管理多個不一樣的鏈接池,提供通一的管理界面。在應用系統中經過 ConnectionManager和相關的配置文件,能夠將凌亂散落在各自應用程序中的數據庫配置信息(包括:數據庫名、用戶、密碼等信息),集中在一個文件中。便於系統的維護工做。

3.3 鏈接池使用範例

  對2.1的標準JDBC的使用範例,改成使用鏈接池,結果以下:

import java.sql.*;
import net.snapbug.util.dbtool.*;
//…
ConnectionPool dbConn = ConnectionManager
.getConnectionPool("testOracle" );
Statement st = dbConn.createStatement();
ResultSet rs = st.executeQuery(
"select * from demo_table" );
//…
some data source operation
in herers.close();st.close();

  在例子中,Snap-ConnectionPool封裝了應用對Connection的管理。只要改變JDBC獲取Connection的方法,爲獲取鏈接池(ConnectionPool)(粗體部分),其餘的數據操做均可以不作修改。按照這樣的方式,Snap- ConnectionPool可幫助應用有效地管理數據庫資源。若是應用忽視了最後資源的釋放: rs.close() 和 st.close(),鏈接池會經過超時(time-out)機制,自動回收。

4.小結

  不管是Snap-ConnectionPool仍是其餘的數據庫鏈接池,都應當具有一下基本功能:

  -對源數據庫資源的保護

  -充分利用發揮數據庫的有效資源

  -簡化應用的數據庫接口,封閉資源管理。

  -對應用遺留資源的自動回收和整理,提升資源的再次利用率。

  在這個前提下,應用程序才能投入更多的精力於各自的業務邏輯中。數據庫資源也再也不成爲系統的瓶頸。

3、Druid鏈接池(新版starter)在SpringBoot下的使用

1.更新pom.xml

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.10</version>
</dependency>

2.編寫application.yml,部分說明寫在註釋了:

spring:
  application:
    name: springboot-test-exam1
  datasource:
    # 使用阿里的Druid鏈接池
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    # 填寫你數據庫的url、登陸名、密碼和數據庫名
    url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8
    username: root
    password: root
    druid:
      # 鏈接池的配置信息
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      maxActive: 20
      # 配置獲取鏈接等待超時的時間
      maxWait: 60000
      # 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一個鏈接在池中最小生存的時間,單位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打開PSCache,而且指定每一個鏈接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置監控統計攔截的filters,去掉後監控界面sql沒法統計,'wall'用於防火牆
      filters: stat,wall,log4j
      # 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      # 配置DruidStatFilter
      web-stat-filter:
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      # 配置DruidStatViewServlet
      stat-view-servlet:
        url-pattern: "/druid/*"
        # IP白名單(沒有配置或者爲空,則容許全部訪問)
        allow: 127.0.0.1,192.168.163.1
        # IP黑名單 (存在共同時,deny優先於allow)
        deny: 192.168.1.73
        #  禁用HTML頁面上的「Reset All」功能
        reset-enable: false
        # 登陸名
        login-username: admin
        # 登陸密碼
        login-password: 123456

爲了方便使用application.properties的讀者,使用下面的配置和上面相同

server.port=8080
 
spring.application.name=springboot-test-exam1
 
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.filters=stat,wall,log4j
spring.datasource.druid.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.allow=127.0.0.1,192.168.163.1
spring.datasource.druid.stat-view-servlet.deny=192.168.1.73
spring.datasource.druid.stat-view-servlet.reset-enable=false
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=123456

運行結果
訪問:http://localhost:8080/druid/,登陸名:admin,密碼123456

clipboard.png

4、參考引用

https://blog.csdn.net/weixin_...
https://www.cnblogs.com/wym78...

相關文章
相關標籤/搜索