轉(zhuǎn)載自:https://www.cnblogs.com/liva-/p/10319055.html
前言:
對(duì)于一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)應(yīng)用潭枣,由于對(duì)于數(shù)據(jù)庫(kù)的訪問不是很頻繁。這時(shí)可以簡(jiǎn)單地在需要訪問數(shù)據(jù)庫(kù)時(shí),就新創(chuàng)建一個(gè)連接,用完后就關(guān)閉它,這樣做也不會(huì)帶來什么明顯的性能上的開銷挺庞。但是對(duì)于一個(gè)復(fù)雜的數(shù)據(jù)庫(kù)應(yīng)用,情況就完全不同了稼病。頻繁的建立选侨、關(guān)閉連接,會(huì)極大的減低系統(tǒng)的性能然走,因?yàn)閷?duì)于連接的使用成了系統(tǒng)性能的瓶頸援制。
? ? 連接復(fù)用。通過建立一個(gè)數(shù)據(jù)庫(kù)連接池以及一套連接使用管理策略芍瑞,使得一個(gè)數(shù)據(jù)庫(kù)連接可以得到高效晨仑、安全的復(fù)用,避免了數(shù)據(jù)庫(kù)連接頻繁建立拆檬、關(guān)閉的開銷洪己。?
? ? 對(duì)于共享資源,有一個(gè)很著名的設(shè)計(jì)模式:資源池竟贯。該模式正是為了解決資源頻繁分配答捕、釋放所造成的問題的。把該模式應(yīng)用到數(shù)據(jù)庫(kù)連接管理領(lǐng)域屑那,就是建立一個(gè)數(shù)據(jù)庫(kù)連接池拱镐,提供一套高效的連接分配晌缘、使用策略,最終目標(biāo)是實(shí)現(xiàn)連接的高效痢站、安全的復(fù)用。?
數(shù)據(jù)庫(kù)連接池的基本原理是在內(nèi)部對(duì)象池中維護(hù)一定數(shù)量的數(shù)據(jù)庫(kù)連接选酗,并對(duì)外暴露數(shù)據(jù)庫(kù)連接獲取和返回方法阵难。如:
外部使用者可通過getConnection 方法獲取連接,使用完畢后再通過releaseConnection 方法將連接返回芒填,注意此時(shí)連接并沒有關(guān)閉呜叫,而是由連接池管理器回收,并為下一次使用做好準(zhǔn)備殿衰。
數(shù)據(jù)庫(kù)連接池技術(shù)帶來的優(yōu)勢(shì):
1. 資源重用?
由于數(shù)據(jù)庫(kù)連接得到重用朱庆,避免了頻繁創(chuàng)建、釋放連接引起的大量性能開銷闷祥。在減少系統(tǒng)消耗的基礎(chǔ)上娱颊,另一方面也增進(jìn)了系統(tǒng)運(yùn)行環(huán)境的平穩(wěn)性(減少內(nèi)存碎片以及數(shù)據(jù)庫(kù)臨時(shí)進(jìn)程/線程的數(shù)量)。
2. 更快的系統(tǒng)響應(yīng)速度?
數(shù)據(jù)庫(kù)連接池在初始化過程中凯砍,往往已經(jīng)創(chuàng)建了若干數(shù)據(jù)庫(kù)連接置于池中備用箱硕。此時(shí)連接的初始化工作均已完成。對(duì)于業(yè)務(wù)請(qǐng)求處理而言悟衩,直接利用現(xiàn)有可用連接剧罩,避免了數(shù)據(jù)庫(kù)連接初始化和釋放過程的時(shí)間開銷,從而縮減了系統(tǒng)整體響應(yīng)時(shí)間座泳。
3. 新的資源分配手段?
對(duì)于多應(yīng)用共享同一數(shù)據(jù)庫(kù)的系統(tǒng)而言惠昔,可在應(yīng)用層通過數(shù)據(jù)庫(kù)連接的配置,實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池技術(shù)挑势,幾年錢也許還是個(gè)新鮮話題镇防,對(duì)于目前的業(yè)務(wù)系統(tǒng)而言,如果設(shè)計(jì)中還沒有考慮到連接池的應(yīng)用薛耻,那么…….快在設(shè)計(jì)文檔中加上這部分的內(nèi)容吧营罢。某一應(yīng)用最大可用數(shù)據(jù)庫(kù)連接數(shù)的限制,避免某一應(yīng)用獨(dú)占所有數(shù)據(jù)庫(kù)資源饼齿。
4. 統(tǒng)一的連接管理饲漾,避免數(shù)據(jù)庫(kù)連接泄漏?
在較為完備的數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)中,可根據(jù)預(yù)先的連接占用超時(shí)設(shè)定缕溉,強(qiáng)制收回被占用連接考传。從而避免了常規(guī)數(shù)據(jù)庫(kù)連接操作中可能出現(xiàn)的資源泄漏。一個(gè)最小化的數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn).
連接池類是對(duì)某一數(shù)據(jù)庫(kù)所有連接的“緩沖池”证鸥,主要實(shí)現(xiàn)以下功能:①?gòu)倪B接池獲取或創(chuàng)建可用連接僚楞;②使用完畢之后勤晚,把連接返還給連接池;③在系統(tǒng)關(guān)閉前泉褐,斷開所有連接并釋放連接占用的系統(tǒng)資源赐写;④還能夠處理無效連接(原來登記為可用的連接,由于某種原因不再可用膜赃,如超時(shí)挺邀,通訊問題),并能夠限制連接池中的連接總數(shù)不低于某個(gè)預(yù)定值和不超過某個(gè)預(yù)定值跳座。
DBCP連接池參數(shù):
參數(shù)默認(rèn)值說明
usernameroot傳遞給JDBC驅(qū)動(dòng)的用于建立連接的用戶名
passwordroot傳遞給JDBC驅(qū)動(dòng)的用于建立連接的密碼
urljdbc:mysql://localhost:3306/adname傳遞給JDBC驅(qū)動(dòng)的用于建立連接的URL
driverClassNamecom.mysql.jdbc.Driver使用的JDBC驅(qū)動(dòng)的完整有效的Java類名
initialSize0初始化連接:連接池啟動(dòng)時(shí)創(chuàng)建的初始化連接數(shù)量,1.2版本后支持
maxActive8最大活動(dòng)連接:連接池在同一時(shí)間能夠分配的最大活動(dòng)連接的數(shù)量, 如果設(shè)置為非正數(shù)則表示不限制
maxIdle8最大空閑連接:連接池中容許保持空閑狀態(tài)的最大連接數(shù)量,超過的空閑連接將被釋放,如果設(shè)置為負(fù)數(shù)表示不限制
minIdle0最小空閑連接:連接池中容許保持空閑狀態(tài)的最小連接數(shù)量,低于這個(gè)數(shù)量將創(chuàng)建新的連接,如果設(shè)置為0則不創(chuàng)建
maxWait無限最大等待時(shí)間:當(dāng)沒有可用連接時(shí),連接池等待連接被歸還的最大時(shí)間(以毫秒計(jì)數(shù))超過時(shí)間則拋出異常,如果設(shè)置為-1表示無限等待
testOnReturnfalse是否在歸還到池中前進(jìn)行檢驗(yàn)
testWhileIdlefalse連接是否被空閑連接回收器(如果有)進(jìn)行檢驗(yàn).如果檢測(cè)失敗,則連接將被從池中去除.設(shè)置為true后如果要生效,validationQuery參數(shù)必須設(shè)置為非空字符串
minEvictableIdleTimeMillis1000 * 60 * 30連接在池中保持空閑而不被空閑連接回收器線程(如果有)回收的最小時(shí)間值端铛,單位毫秒
numTestsPerEvictionRun3在每次空閑連接回收器線程(如果有)運(yùn)行時(shí)檢查的連接數(shù)量
timeBetweenEvictionRunsMillis-1在空閑連接回收器線程運(yùn)行期間休眠的時(shí)間值,以毫秒為單位.如果設(shè)置為非正數(shù),則不運(yùn)行空閑連接回收器線程
validationQuerynullSQL查詢,用來驗(yàn)證從連接池取出的連接,在將連接返回給調(diào)用者之前.如果指定,則查詢必須是一個(gè)SQL SELECT并且必須返回至少一行記錄
testOnBorrowtrue是否在從池中取出連接前進(jìn)行檢驗(yàn),如果檢驗(yàn)失敗,則從池中去除連接并嘗試取出另一個(gè).
DBCP(DataBase Connection Pool)數(shù)據(jù)庫(kù)連接池,是Java數(shù)據(jù)庫(kù)連接池的一種疲眷,由Apache開發(fā)禾蚕,通過數(shù)據(jù)庫(kù)連接池,可以讓程序自動(dòng)管理數(shù)據(jù)庫(kù)連接的釋放和斷開狂丝。由于建立數(shù)據(jù)庫(kù)連接是一種非常耗時(shí)换淆、耗資源的行為,所以通過連接池預(yù)先同數(shù)據(jù)庫(kù)建立一些連接美侦,放在內(nèi)存中产舞,應(yīng)用程序需要建立數(shù)據(jù)庫(kù)連接時(shí)直接到連接池中申請(qǐng)一個(gè)就行,使用完畢后再歸還到連接池中菠剩。
dbcp所依賴的jar包:
對(duì)應(yīng)的properties文件:
Druid配置參數(shù):
配置缺省值說明
name?配置這個(gè)屬性的意義在于易猫,如果存在多個(gè)數(shù)據(jù)源,監(jiān)控的時(shí)候可以通過名字來區(qū)分開來具壮。?如果沒有配置准颓,將會(huì)生成一個(gè)名字,格式是:"DataSource-" + System.identityHashCode(this)
jdbcUrl?連接數(shù)據(jù)庫(kù)的url棺妓,不同數(shù)據(jù)庫(kù)不一樣攘已。例如:?mysql : jdbc:mysql://10.20.153.104:3306/druid2?oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username?連接數(shù)據(jù)庫(kù)的用戶名
password?連接數(shù)據(jù)庫(kù)的密碼。如果你不希望密碼直接寫在配置文件中怜跑,可以使用ConfigFilter样勃。詳細(xì)看這里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName根據(jù)url自動(dòng)識(shí)別這一項(xiàng)可配可不配,如果不配置druid會(huì)根據(jù)url自動(dòng)識(shí)別dbType性芬,然后選擇相應(yīng)的driverClassName(建議配置下)
initialSize0初始化時(shí)建立物理連接的個(gè)數(shù)峡眶。初始化發(fā)生在顯示調(diào)用init方法,或者第一次getConnection時(shí)
maxActive8最大連接池?cái)?shù)量
maxIdle8已經(jīng)不再使用植锉,配置了也沒效果
minIdle?最小連接池?cái)?shù)量
maxWait?獲取連接時(shí)最大等待時(shí)間辫樱,單位毫秒。配置了maxWait之后俊庇,缺省啟用公平鎖狮暑,并發(fā)效率會(huì)有所下降鸡挠,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
poolPreparedStatementsfalse是否緩存preparedStatement搬男,也就是PSCache拣展。PSCache對(duì)支持游標(biāo)的數(shù)據(jù)庫(kù)性能提升巨大,比如說oracle缔逛。在mysql下建議關(guān)閉瞎惫。
maxOpenPreparedStatements-1要啟用PSCache,必須配置大于0译株,當(dāng)大于0時(shí),poolPreparedStatements自動(dòng)觸發(fā)修改為true挺益。在Druid中歉糜,不會(huì)存在Oracle下PSCache占用內(nèi)存過多的問題,可以把這個(gè)數(shù)值配置大一些望众,比如說100
validationQuery?用來檢測(cè)連接是否有效的sql匪补,要求是一個(gè)查詢語(yǔ)句。如果validationQuery為null烂翰,testOnBorrow夯缺、testOnReturn、testWhileIdle都不會(huì)其作用甘耿。
testOnBorrowtrue申請(qǐng)連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效踊兜,做了這個(gè)配置會(huì)降低性能。
testOnReturnfalse歸還連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效佳恬,做了這個(gè)配置會(huì)降低性能
testWhileIdlefalse建議配置為true捏境,不影響性能,并且保證安全性毁葱。申請(qǐng)連接的時(shí)候檢測(cè)垫言,如果空閑時(shí)間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測(cè)連接是否有效倾剿。
timeBetweenEvictionRunsMillis?有兩個(gè)含義:?1) Destroy線程會(huì)檢測(cè)連接的間隔時(shí)間2) testWhileIdle的判斷依據(jù)筷频,詳細(xì)看testWhileIdle屬性的說明
numTestsPerEvictionRun?不再使用,一個(gè)DruidDataSource只支持一個(gè)EvictionRun
minEvictableIdleTimeMillis??
connectionInitSqls?物理連接初始化的時(shí)候執(zhí)行的sql
exceptionSorter根據(jù)dbType自動(dòng)識(shí)別當(dāng)數(shù)據(jù)庫(kù)拋出一些不可恢復(fù)的異常時(shí)前痘,拋棄連接
filters?屬性類型是字符串凛捏,通過別名的方式配置擴(kuò)展插件,常用的插件有:?監(jiān)控統(tǒng)計(jì)用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters?類型是List<com.alibaba.druid.filter.Filter>际度,如果同時(shí)配置了filters和proxyFilters葵袭,是組合關(guān)系,并非替換關(guān)系
Druid是目前最好的數(shù)據(jù)庫(kù)連接池乖菱,在功能坡锡、性能蓬网、擴(kuò)展性方面,都超過其他數(shù)據(jù)庫(kù)連接池鹉勒,包括DBCP帆锋、C3P0、BoneCP禽额、Proxool锯厢、JBoss DataSource。
所依賴的jar包:
對(duì)應(yīng)的properties文件:
dbcp和druid代碼對(duì)比:
1.druid比dbcp少了一個(gè)最大空閑的參數(shù)
2.需注意properties文件里的5678行名字脯倒,對(duì)應(yīng)改回來
?
相應(yīng)代碼:
publicclassJDBCUtil{// 建立連接的驅(qū)動(dòng)驅(qū)動(dòng)名稱publicstaticString DRIVER_CLASS_NAME ="";// 數(shù)據(jù)庫(kù)鏈接數(shù)據(jù)哭的urlpublicstaticString URL ="";// 鏈接的數(shù)據(jù)庫(kù)賬號(hào)publicstaticString USERNAME ="";// 鏈接的數(shù)據(jù)庫(kù)密碼publicstaticString PASSWORD ="";// 醉的等待時(shí)間privatestaticlongMAX_WAIT;// 最大活動(dòng)鏈接privatestaticintMAX_ACTIVE;// 初始化時(shí)鏈接池的數(shù)量privatestaticintINITIAL_SIZE;publicstaticProperties properties =newProperties();publicstaticDruidDataSource druidDataSource =newDruidDataSource();static{InputStream ips = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");try{properties.load(ips);}catch(IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 賦值操作DRIVER_CLASS_NAME = properties.getProperty("jdbc.driver");URL = properties.getProperty("jdbc.url");USERNAME = properties.getProperty("jdbc.username");PASSWORD = properties.getProperty("jdbc.password");MAX_WAIT = Long.valueOf(properties.getProperty("druid.maxwait"));MAX_ACTIVE = Integer.valueOf(properties.getProperty("druid.maxactive"));INITIAL_SIZE = Integer.valueOf(properties.getProperty("druid.initsize"));// 給連接池初始化參數(shù)druidDataSource.setDriverClassName(DRIVER_CLASS_NAME);druidDataSource.setUrl(URL);druidDataSource.setUsername(USERNAME);druidDataSource.setPassword(PASSWORD);druidDataSource.setInitialSize(INITIAL_SIZE);druidDataSource.setMaxActive(MAX_ACTIVE);druidDataSource.setMaxWait(MAX_WAIT);}publicstaticDruidDataSourcegetDataSources(){returndruidDataSource;}publicstaticConnectiongetConnection(){try{returnbasicDataSource.getConnection();}catch(SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}returnnull;}//測(cè)試連接publicstaticvoidmain(String[] args){System.out.println(properties);}//測(cè)試連接是否成功的main方法publicstaticvoidmain(String[] args) throws Exception{Connection connection=druidDataSource.getConnection();String sql="select * from t_user";PreparedStatement statement=connection.prepareStatement(sql);ResultSet rs=statement.executeQuery();while(rs.next()) {System.out.println(rs.getString("username"));}}}