# 數(shù)據(jù)庫連接池
### 什么是連接池
數(shù)據(jù)庫連接池負責<font color='red'>分配蝶押、管理和釋放數(shù)據(jù)庫連接</font>狭园,它允許應(yīng)用程序<font color='red'>重復使用</font>一個現(xiàn)有的數(shù)據(jù)庫連接沙咏,<font color='orange'>而不是再重新建立一個</font>
### 為什么要用連接池
一個數(shù)據(jù)庫連接對象均對應(yīng)一個物理數(shù)據(jù)庫連接宴胧,每次操作都打開一個物理連接晶姊,使用完都關(guān)閉連接,<font color='cornflowerblue'>這樣造成系統(tǒng)的性能低下</font>崩瓤。
![img](池.assets/1606768-20190616155729169-544894466.png)
數(shù)據(jù)庫連接池的解決方案是在應(yīng)用程序啟動時建立足夠的數(shù)據(jù)庫連接袍啡,并講這些連接組成一個連接池,由應(yīng)用程序動態(tài)地對池中的連接進行申請却桶、使用和釋放境输。
<font color='red'>連接池技術(shù)盡可能多地重用了消耗內(nèi)存地資源,大大節(jié)省了內(nèi)存颖系,提高了服務(wù)器地服務(wù)效率嗅剖,能夠支持更多的客戶服務(wù)。</font>通過使用連接池嘁扼,將<font color='orange'>大大提高程序運行效率</font>信粮,同時,我們可以通過其自身的管理機制來監(jiān)視數(shù)據(jù)庫連接的數(shù)量趁啸、使用情況等强缘。
### 數(shù)據(jù)庫連接池接口
Java 為數(shù)據(jù)庫連接池提供了公共接口:<font color='red'>javax.sql.DataSource</font>,各大廠商可以讓自己的連接池實現(xiàn)該接口不傅,而應(yīng)用程序可以方便的切換不同的連接池旅掂。
### 數(shù)據(jù)庫連接池的優(yōu)點
- <font color='red'>資源重用</font>
? 由于數(shù)據(jù)庫連接得到重用,避免了頻繁創(chuàng)建访娶、釋放連接引起的大量性能開銷商虐。在減少系統(tǒng)消耗的基礎(chǔ)上,另一方面也增進了系統(tǒng)運行環(huán)境的平穩(wěn)性(減少內(nèi)存碎片以及數(shù)據(jù)庫臨時進程/線程的數(shù)量)崖疤。
- <font color='red'>更快的系統(tǒng)響應(yīng)速度</font>
? 數(shù)據(jù)庫連接池在初始化過程中秘车,往往已經(jīng)創(chuàng)建了若干數(shù)據(jù)庫連接置于池中備用。此時連接的初始化工作均已完成戳晌。對于業(yè)務(wù)請求處理而言鲫尊,直接利用現(xiàn)有可用連接,避免了數(shù)據(jù)庫連接初始化和釋放過程的時間開銷沦偎,從而縮減了系統(tǒng)整體響應(yīng)時間疫向。
- <font color='red'>新的資源分配手段</font>
? 對于多應(yīng)用共享同一數(shù)據(jù)庫的系統(tǒng)而言,可在應(yīng)用層通過數(shù)據(jù)庫連接的配置豪嚎,實現(xiàn)數(shù)據(jù)庫連接池技術(shù)搔驼,幾年錢也許還是個新鮮話題,對于目前的業(yè)務(wù)系統(tǒng)而言侈询,如果設(shè)計中還沒有考慮到連接池的應(yīng)用舌涨。某一應(yīng)用最大可用數(shù)據(jù)庫連接數(shù)的限制,避免某一應(yīng)用獨占所有數(shù)據(jù)庫資源扔字。
- <font color='red'>統(tǒng)一的連接管理囊嘉,避免數(shù)據(jù)庫連接泄漏</font>
? 在較為完備的數(shù)據(jù)庫連接池實現(xiàn)中温技,可根據(jù)預先的連接占用超時設(shè)定,強制收回被占用連接扭粱。從而避免了常規(guī)數(shù)據(jù)庫連接操作中可能出現(xiàn)的資源泄漏舵鳞。
### 數(shù)據(jù)庫連接池的工作原理
分位3步:
- 連接池的建立
- 連接池中連接的使用管理
- 連接池的關(guān)閉
<font color='red'>連接池的建立。</font>一般在系統(tǒng)初始化時琢蛤,連接池會根據(jù)系統(tǒng)配置建立蜓堕,并在池中創(chuàng)建了幾個連接對象,以便使用時能從連接池中獲取博其。<font color='cornflowerblue'>連接池中的連接不能隨意創(chuàng)建和關(guān)閉</font>套才,這樣避免了連接隨意建立和關(guān)閉造成的系統(tǒng)開銷。
<font color='red'>連接池的管理慕淡。</font>連接池管理策略是連接池機制的核心背伴,連接池內(nèi)連接的分配和釋放對系統(tǒng)的性能有很大的影響。其管理策略是:
```oop
當客戶請求數(shù)據(jù)庫連接時儡率,首先查看連接池中是否有空閑連接挂据,如果存在空閑連接,則將連接分配給客戶使用儿普;如果沒有空閑連接崎逃,則查看當前所開的連接數(shù)是否已經(jīng)達到最大連接數(shù),如果沒達到就重新創(chuàng)建一個連接給請求的客戶眉孩;如果達到就按設(shè)定的最大等待時間進行等待个绍,如果超出最大等待時間,則拋出異常給客戶浪汪。
當客戶釋放數(shù)據(jù)庫連接時巴柿,先判斷該連接的引用次數(shù)是否超過了規(guī)定值,如果超過就從連接池中刪除該連接死遭,否則保留為其他客戶服務(wù)广恢。
該策略保證了數(shù)據(jù)庫連接的有效復用,避免頻繁的建立呀潭、釋放連接所帶來的系統(tǒng)資源開銷钉迷。
```
<font color='red'>連接池的關(guān)閉。</font>當應(yīng)用程序退出時钠署,關(guān)閉連接池中所有的連接糠聪,釋放連接池相關(guān)的資源,以便連接可以返回池中重復利用谐鼎。我們可以通過<font color='orange'>Connection</font>對象的<font color='orange'>Close</font>或<font color='orange'>Dispose</font>方法
## 常用的連接池
| 數(shù)據(jù)庫連接池 | 最新版本? ? | 發(fā)布時間 |
| ------------ | ------------ | -------- |
| c3p0? ? ? ? | c3p0-0.9.5.2 | 2015? ? |
| dbcp? ? ? ? | 2.2.0? ? ? ? | 2017? ? |
| **druid**? ? | 0.11.0? ? ? | 2017? ? |
| **HikariCP** | 2.7.6? ? ? ? | 2018? ? |
#### C3P0
<font color='red'>C3P0</font>在很長一段時間內(nèi)舰蟆,它一直是Java領(lǐng)域內(nèi)數(shù)據(jù)庫連接池的代名詞,當年盛極一時的<font color='red'>Hibernate</font>都將其作為內(nèi)置的數(shù)據(jù)庫連接池,可以業(yè)內(nèi)對它的穩(wěn)定性還是認可的身害。<font color='orange'>C3P0功能簡單易用味悄,穩(wěn)定性好這是它的優(yōu)點,但是性能上的缺點卻讓它徹底被打入冷宮</font>题造。<font color='red'>C3P0的性能很差</font>傍菇,差到即便是同時代的產(chǎn)品相比它也是墊底的
#### DBCP
屬于Apache頂級項目Commons中的核心子項目(最早在Jakarta Commons里就有),在Apache的生態(tài)圈中的影響里十分廣泛,比如最為大家所熟知的Tomcat就在內(nèi)部集成了DBCP界赔,實現(xiàn)JPA規(guī)范的<font color='orange'>OpenJPA</font>,也是默認集成DBCP的牵触。但DBCP并不是獨立實現(xiàn)連接池功能的淮悼,它內(nèi)部依賴于Commons中的另一個子項目Pool,連接池最核心的“池”揽思,就是由Pool組件提供的袜腥,因為核心功能依賴于Pool,所以DBCP本身只能做小版本的更新钉汗,真正大版本的更迭則完全依托于pool羹令。
#### 性能無敵的<font color='red'>HikariCP</font>
- 字節(jié)碼精簡:優(yōu)化代碼,直到編譯后的字節(jié)碼最少损痰,這樣福侈,CPU緩存可以加載更多的程序代碼;
- 優(yōu)化代理和攔截器:減少代碼卢未,例如<font color='orange'>HikariCP</font>的Statement proxy只有100行代碼肪凛;
- 自定義數(shù)組類型(<font color='orange'>FastStatementList</font>)代替<font color='orange'>ArrayList</font>:避免每次get()調(diào)用都要進行range check,避免調(diào)用remove()時的從頭到尾的掃描辽社;
- 自定義集合類型(<font color='orange'>ConcurrentBag</font>):提高并發(fā)讀寫的效率伟墙;
- 其他缺陷的優(yōu)化,比如對于耗時超過一個CPU時間片的方法調(diào)用的研究(但沒說具體怎么優(yōu)化)滴铅。
java代碼
```Java
@Test
? ? //Hikari
? ? public void test3() throws Exception{
? ? ? ? Properties properties = new Properties();
? ? ? ? properties.load(PoolTest.class.getClassLoader().getResourceAsStream("hikari.properties"));
? ? ? ? HikariConfig hikariConfig = new HikariConfig(properties);
? ? ? ? DataSource dataSource = new HikariDataSource(hikariConfig);
? ? ? ? System.out.println(dataSource.getConnection());
? ? }
```
properties配置文件
```properties
jdbcUrl=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=root
driverClassName=com.mysql.cj.jdbc.Driver
```
#### 功能全面的<font color='red'>Druid</font>(德魯伊)
Druid 相對于其他數(shù)據(jù)庫連接池的優(yōu)點
- 強大的監(jiān)控特性戳葵,通過Druid提供的監(jiān)控功能,可以清楚知道連接池和SQL的工作情況汉匙。
- 監(jiān)控SQL的執(zhí)行時間拱烁、<font color='orange'>ResultSet</font>持有時間、返回行數(shù)盹兢、更新行數(shù)邻梆、錯誤次數(shù)、錯誤堆棧信息绎秒;
-? SQL執(zhí)行的耗時區(qū)間分布浦妄。什么是耗時區(qū)間分布呢?比如說,某個SQL執(zhí)行了1000次剂娄,其中0\~1毫秒?yún)^(qū)間50次蠢涝,1\~10毫秒800次,10\~100毫秒100次阅懦,100\~1000毫秒30次和二,1~10秒15次,10秒以上5次耳胎。通過耗時區(qū)間分布果善,能夠非常清楚知道SQL的執(zhí)行耗時情況;
-? 監(jiān)控連接池的物理連接創(chuàng)建和銷毀次數(shù)褒墨、邏輯連接的申請和關(guān)閉次數(shù)震贵、非空等待次數(shù)、<font color='orange'>PSCache</font>命中率等郁惜。
- 方便擴展堡距。Druid提供了Filter-Chain模式的擴展API,可以自己編寫Filter攔截JDBC中的任何方法兆蕉,可以在上面做任何事情羽戒,比如說性能監(jiān)控、SQL審計虎韵、用戶名密碼加密易稠、日志等等。
<font color='red'>Druid</font>集合了開源和商業(yè)數(shù)據(jù)庫連接池的優(yōu)秀特性劝术,并結(jié)合<font color='red'>阿里巴巴</font>大規(guī)乃醵啵苛刻生產(chǎn)環(huán)境的使用經(jīng)驗進行優(yōu)化。
| 配置? ? ? ? ? ? ? ? ? ? ? ? ? | 缺省值? ? ? ? ? ? | 說明? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| ----------------------------- | ------------------ | ------------------------------------------------------------ |
| name? ? ? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 配置這個屬性的意義在于养晋,如果存在多個數(shù)據(jù)源衬吆,監(jiān)控的時候? 可以通過名字來區(qū)分開來。如果沒有配置绳泉,將會生成一個名字逊抡,? 格式是:"DataSource-" + System.identityHashCode(this) |
| jdbcUrl? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 連接數(shù)據(jù)庫的url,不同數(shù)據(jù)庫不一樣零酪。例如:? mysql : jdbc:mysql://10.20.153.104:3306/druid2? oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
| username? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 連接數(shù)據(jù)庫的用戶名? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| password? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 連接數(shù)據(jù)庫的密碼冒嫡。如果你不希望密碼直接寫在配置文件中,? 可以使用ConfigFilter四苇。詳細看這里:? [https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter](https://github.com/alibaba/druid/wiki/使用ConfigFilter) |
| driverClassName? ? ? ? ? ? ? | 根據(jù)url自動識別? ? | 這一項可配可不配孝凌,如果不配置druid會根據(jù)url自動識別dbType,然后選擇相應(yīng)的driverClassName |
| initialSize? ? ? ? ? ? ? ? ? | 0? ? ? ? ? ? ? ? ? | 初始化時建立物理連接的個數(shù)月腋。初始化發(fā)生在顯示調(diào)用init方法蟀架,或者第一次getConnection時 |
| maxActive? ? ? ? ? ? ? ? ? ? | 8? ? ? ? ? ? ? ? ? | 最大連接池數(shù)量? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| maxIdle? ? ? ? ? ? ? ? ? ? ? | 8? ? ? ? ? ? ? ? ? | 已經(jīng)不再使用瓣赂,配置了也沒效果? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| minIdle? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 最小連接池數(shù)量? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| maxWait? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 獲取連接時最大等待時間,單位毫秒片拍。配置了maxWait之后煌集,? 缺省啟用公平鎖,并發(fā)效率會有所下降捌省,? 如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖苫纤。 |
| poolPreparedStatements? ? ? ? | false? ? ? ? ? ? ? | 是否緩存preparedStatement,也就是PSCache纲缓。? PSCache對支持游標的數(shù)據(jù)庫性能提升巨大卷拘,比如說oracle。? 在mysql5.5以下的版本中沒有PSCache功能祝高,建議關(guān)閉掉恭金。 作者在5.5版本中使用PSCache,通過監(jiān)控界面發(fā)現(xiàn)PSCache有緩存命中率記錄褂策,? 該應(yīng)該是支持PSCache。 |
| maxOpenPreparedStatements? ? | -1? ? ? ? ? ? ? ? | 要啟用PSCache颓屑,必須配置大于0斤寂,當大于0時,? poolPreparedStatements自動觸發(fā)修改為true揪惦。? 在Druid中遍搞,不會存在Oracle下PSCache占用內(nèi)存過多的問題,? 可以把這個數(shù)值配置大一些器腋,比如說100 |
| validationQuery? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 用來檢測連接是否有效的sql溪猿,要求是一個查詢語句。? 如果validationQuery為null纫塌,testOnBorrow诊县、testOnReturn、? testWhileIdle都不會其作用措左。 |
| testOnBorrow? ? ? ? ? ? ? ? ? | true? ? ? ? ? ? ? | 申請連接時執(zhí)行validationQuery檢測連接是否有效依痊,做了這個配置會降低性能。 |
| testOnReturn? ? ? ? ? ? ? ? ? | false? ? ? ? ? ? ? | 歸還連接時執(zhí)行validationQuery檢測連接是否有效怎披,做了這個配置會降低性能 |
| testWhileIdle? ? ? ? ? ? ? ? | false? ? ? ? ? ? ? | 建議配置為true胸嘁,不影響性能,并且保證安全性凉逛。? 申請連接的時候檢測性宏,如果空閑時間大于? timeBetweenEvictionRunsMillis,? 執(zhí)行validationQuery檢測連接是否有效状飞。 |
| timeBetweenEvictionRunsMillis |? ? ? ? ? ? ? ? ? ? | 有兩個含義:? 1) Destroy線程會檢測連接的間隔時間? 2) testWhileIdle的判斷依據(jù)毫胜,詳細看testWhileIdle屬性的說明 |
| numTestsPerEvictionRun? ? ? ? |? ? ? ? ? ? ? ? ? ? | 不再使用书斜,一個DruidDataSource只支持一個EvictionRun? ? ? ? ? |
| minEvictableIdleTimeMillis? ? |? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| connectionInitSqls? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 物理連接初始化的時候執(zhí)行的sql? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
| exceptionSorter? ? ? ? ? ? ? | 根據(jù)dbType自動識別 | 當數(shù)據(jù)庫拋出一些不可恢復的異常時,拋棄連接? ? ? ? ? ? ? ? ? |
| filters? ? ? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 屬性類型是字符串指蚁,通過別名的方式配置擴展插件菩佑,? 常用的插件有:? 監(jiān)控統(tǒng)計用的filter:stat? 日志用的filter:log4j? 防御sql注入的filter:wall |
| proxyFilters? ? ? ? ? ? ? ? ? |? ? ? ? ? ? ? ? ? ? | 類型是List<com.alibaba.druid.filter.Filter>,? 如果同時配置了filters和proxyFilters凝化,? 是組合關(guān)系稍坯,并非替換關(guān)系 |
Druid java代碼
```Java
public void test1() throws Exception{
? ? ? ? Properties properties = new Properties();
? ? ? ? properties.load(PoolTest.class.getClassLoader().getResourceAsStream("jdbc.properties"));
? ? ? ? DruidDataSource druidDataSource = new DruidDataSource();
? ? ? ? druidDataSource.configFromPropety(properties);
? ? ? ? Connection connection = druidDataSource.getConnection();
? ? ? ? System.out.println(connection);
? ? }
```
Druid properties配置文件
```properties
druid.url=jdbc:mysql://127.0.0.1:3306/job?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
druid.username=root
druid.password=root
druid.driverClassName=com.mysql.cj.jdbc.Driver
```