日常啰嗦
終于回到既定軌道上了,這一篇講講數(shù)據(jù)庫連接池的相關(guān)知識,線程池以后有機會再結(jié)合項目單獨寫篇文章(自己給自己挖坑,不知道什么時候能填上),從這一篇文章開始到本階段結(jié)束的文章都會圍繞數(shù)據(jù)庫和dao層的優(yōu)化去寫,本篇是一個開始。本文會介紹連接池技術(shù)并對比目前比較流行的java連接池技術(shù),之后,會把druid整合到項目中來,將技術(shù)方案落地,實際整合到項目中,讓技術(shù)能為我所用。
使用連接池的原因
jdbc的demo
//第一步,注冊驅(qū)動程序
//com.MySQL.jdbc.Driver
Class.forName("數(shù)據(jù)庫驅(qū)動的完整類名");
//第二步,獲取一個數(shù)據(jù)庫的連接
Connection conn = DriverManager.getConnection("數(shù)據(jù)庫地址","用戶名","密碼");
//第三步,創(chuàng)建一個會話
Statement stmt=conn.createStatement();
//第四步,執(zhí)行SQL語句
stmt.executeUpdate("SQL語句");
//或者查詢記錄
ResultSet rs = stmt.executeQuery("查詢記錄的SQL語句");
//第五步,對查詢的結(jié)果進(jìn)行處理
while(rs.next()){
//操作
}
//第六步,關(guān)閉連接
rs.close();
stmt.close();
conn.close();
對上面幾行代碼,大家不會陌生,這可能是我們初學(xué)jdbc連接時最熟悉的代碼了,雖然現(xiàn)在可能用到了一些數(shù)據(jù)層ORM框架,但是底層實現(xiàn)依然如同上面代碼一樣,只是做了一些封裝而已。這種方式也有一些不足,在與mysql進(jìn)行數(shù)據(jù)交互時每次都需要新建connection資源,用完后關(guān)閉掉Connection資源,這種做法是非常浪費資源的,如果抬杠的話,可能有人會說,我就喜歡這種方式,我服務(wù)器配置足夠好,我一點都不擔(dān)心什么資源不資源,那你牛逼。
浪費資源這種說法是相對而言的,如果在小型項目中或者項目與數(shù)據(jù)庫的交互不那么頻繁的話,數(shù)據(jù)庫連接的創(chuàng)建與關(guān)閉也不見得會把資源浪費多少,亦或者在項目啟動時期,我們可能只考慮功能實現(xiàn),就直接copy一份現(xiàn)成的數(shù)據(jù)庫集成代碼過來用,這種做法都是很正常的現(xiàn)象,本階段的內(nèi)容主要是項目優(yōu)化,那么關(guān)注點肯定就不是項目初始時期,也不去討論硬件配置有多好,而是針對目前代碼中的不足進(jìn)行優(yōu)化,找到一個相對較好的優(yōu)化方案,并落實到項目中去,今天我們講的優(yōu)化方案就是使用連接池技術(shù)代替目前與數(shù)據(jù)庫的交互方式。
為什么在連接數(shù)據(jù)庫時要使用連接池?
數(shù)據(jù)庫連接是一種關(guān)鍵的有限的昂貴的資源,一個數(shù)據(jù)庫連接對象均對應(yīng)一個物理數(shù)據(jù)庫連接,每次操作都打開一個物理連接,使用完都關(guān)閉連接,這樣造成系統(tǒng)的性能低下。
數(shù)據(jù)庫連接池的解決方案是在應(yīng)用程序啟動時建立足夠的數(shù)據(jù)庫連接,并將這些連接組成一個連接池,簡單的說,就是在一個"池"里放了好多半成品的數(shù)據(jù)庫連接對象,由應(yīng)用程序動態(tài)地對池中的連接進(jìn)行申請、使用和釋放等操作。
連接池技術(shù)盡可能多地重用了消耗內(nèi)存地資源,大大節(jié)省了內(nèi)存,提高了服務(wù)器的服務(wù)效率,減少了程序與數(shù)據(jù)庫交互時的部分開銷,顯著的改善應(yīng)用程序的性能,通過使用連接池,提高了程序運行效率,同時,我們可以通過其自身的管理機制來監(jiān)視數(shù)據(jù)庫連接的數(shù)量、使用情況等。
連接池的工作原理
連接池技術(shù)的核心思想是連接復(fù)用(也是我們在前一篇文章中提到的資源重用),通過建立一個數(shù)據(jù)庫連接池以及一套連接使用、分配和管理策略,使得該連接池中的連接可以得到高效、安全的復(fù)用,避免了數(shù)據(jù)庫連接頻繁建立、關(guān)閉的開銷。
連接池的工作原理主要由三部分組成,分別為連接池的建立、連接池中連接的使用管理、連接池的關(guān)閉:
第一、連接池的建立。一般在系統(tǒng)初始化時,連接池會根據(jù)系統(tǒng)配置建立,并在池中創(chuàng)建了幾個連接對象,以便使用時能從連接池中獲取。連接池中的連接不能隨意創(chuàng)建和關(guān)閉,這樣避免了連接隨意建立和關(guān)閉造成的系統(tǒng)開銷。Java中提供了很多容器類可以方便的構(gòu)建連接池,例如Vector、Stack等。
-
第二、連接池的管理。連接池管理策略是連接池機制的核心,連接池內(nèi)連接的分配和釋放對系統(tǒng)的性能有很大的影響。其管理策略是:
當(dāng)客戶請求數(shù)據(jù)庫連接時,首先查看連接池中是否有空閑連接,如果存在空閑連接,則將連接分配給客戶使用;
如果沒有空閑連接,則查看當(dāng)前所開的連接數(shù)是否已經(jīng)達(dá)到最大連接數(shù),如果沒達(dá)到就重新創(chuàng)建一個連接給請求的客戶;
如果達(dá)到就按設(shè)定的最大等待時間進(jìn)行等待,如果超出最大等待時間,則拋出異常給客戶。
-
當(dāng)客戶釋放數(shù)據(jù)庫連接時,先判斷該連接的引用次數(shù)是否超過了規(guī)定值,如果超過就從連接池中刪除該連接,否則保留為其他客戶服務(wù)。
該策略保證了數(shù)據(jù)庫連接的有效復(fù)用,避免頻繁的建立、釋放連接所帶來的系統(tǒng)資源開銷。
第三、連接池的關(guān)閉。當(dāng)應(yīng)用程序退出時,關(guān)閉連接池中所有的連接,釋放連接池相關(guān)的資源,該過程正好與創(chuàng)建相反。
連接池的優(yōu)點及流行的連接池技術(shù)
對于連接池的優(yōu)點,通過前文描述,我們也能得出以下結(jié)論:
- 減少連接創(chuàng)建時間。連接池中的連接是已準(zhǔn)備好的、可重復(fù)使用的,獲取后可以直接訪問數(shù)據(jù)庫,因此減少了連接創(chuàng)建的次數(shù)和時間。
- 簡化的編程模式。當(dāng)使用連接池時,每一個單獨的線程能夠像創(chuàng)建一個自己的JDBC連接一樣操作,允許用戶直接使用JDBC編程技術(shù)。
- 控制資源的使用。如果不使用連接池,每次訪問數(shù)據(jù)庫都需要創(chuàng)建一個連接,這樣系統(tǒng)的穩(wěn)定性受系統(tǒng)連接需求影響很大,很容易產(chǎn)生資源浪費和高負(fù)載異常。連接池能夠使性能最大化,將資源利用控制在一定的水平之下。連接池能控制池中的連接數(shù)量,增強了系統(tǒng)在大流量沖擊下的穩(wěn)定性。
流行的Java連接池:
C3P0是一個開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發(fā)布,包括了實現(xiàn)jdbc3和jdbc2擴(kuò)展規(guī)范說明的Connection 和Statement 池的DataSources 對象。
DBCP (Database Connection Pool)是一個依賴Jakarta commons-pool對象池機制的數(shù)據(jù)庫連接池,Tomcat的數(shù)據(jù)源使用的就是DBCP。目前 DBCP 有兩個版本分別是 1.3 和 1.4。1.3 版本對應(yīng)的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本對應(yīng) JDK 1.6 和 JDBC 4。因此在選擇版本的時候要看看你用的是什么 JDK 版本了,功能上倒是沒有什么區(qū)別。
Proxool是一個Java SQL Driver驅(qū)動程序,提供了對你選擇的其它類型的驅(qū)動程序的連接池封裝。可以非常簡單的移植到現(xiàn)存的代碼中。完全可配置。快速,成熟,健壯。可以透明地為你現(xiàn)存的JDBC驅(qū)動程序增加連接池功能
Druid是阿里開源的一個數(shù)據(jù)庫連接池技術(shù),號稱是目前最好的數(shù)據(jù)庫連接池,在功能、性能、擴(kuò)展性方面,都超過其他數(shù)據(jù)庫連接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已經(jīng)在阿里巴巴部署了超過600個應(yīng)用,經(jīng)過一年多生產(chǎn)環(huán)境大規(guī)模部署的嚴(yán)苛考驗。
對比下來,最終選擇了Druid連接池,接下來也會將Druid技術(shù)整合到目前的項目中去,C3P0和DBCP都用過,也碰到一些問題,總體來說,功能中規(guī)中矩,比直接操作jdbc方式要好的多,但是對比與Druid的話就有些不足了,因為Druid是在目前市面上流行的連接池技術(shù)的基礎(chǔ)上開發(fā)出來的,你有的Druid有,你沒有的Druid也能提供,Druid不僅僅是一個高效可管理的數(shù)據(jù)庫連接池,它還有一套基于Filter-Chain模式的插件體系,也內(nèi)置SQLParser功能,同時還能監(jiān)控數(shù)據(jù)庫訪問性能,可以作為監(jiān)控來使用,總結(jié)起來就是高效、功能強大、可擴(kuò)展性好。
總結(jié):目前的問題及解決方案
目前ssm-demo項目中與mysql服務(wù)器的交互使用的是Spring自帶的一個工具類,DriverManagerDataSource,配置文件如下:
<!-- 配置數(shù)據(jù)源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/ssm_db?useUnicode=true&characterEncoding=UTF-8 "/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</bean>
DriverManagerDataSource建立連接的作法是只要有連接就新建一個connection,根本沒有連接池的作用,也就是說文章前面所提到的資源消耗的弊端還是存在的,但是我們覺察不出來,感覺項目也挺正常的。這是因為目前網(wǎng)站的訪問量較小,與mysql數(shù)據(jù)庫的交互不頻繁,由于訪問量小,對后端的請求就少,因此mysql查詢就少,壓力也不會大,項目體量及訪問總量都很小,就更別提數(shù)據(jù)庫的QPS了,根本不值一提,也就是說現(xiàn)有的網(wǎng)站形勢下,根本不會對mysql數(shù)據(jù)庫產(chǎn)生任何的壓力,根本不會有死鎖的產(chǎn)生,根本不會有事務(wù)鎖的產(chǎn)生,根本不會有數(shù)據(jù)庫資源耗盡情況的產(chǎn)生,也根本不會有數(shù)據(jù)庫服務(wù)器崩掉的產(chǎn)生.....
但是如果網(wǎng)站的訪問量大了起來,功能豐富了起來,用戶訪問量增長了起來,是目前的10000倍、100000倍甚至更大的情況下,各種問題就隨之而來了,當(dāng)然,這么多問題出現(xiàn)了,我們是不是修改了數(shù)據(jù)連接池就好了?肯定不是,數(shù)據(jù)庫連接池僅僅解決了數(shù)據(jù)層的部分問題,對性能有一部分的提升,它不能解決掉網(wǎng)站演進(jìn)過程中的各種問題,別想太多,因為過程中會出現(xiàn)各種各樣的問題,項目也會暴露出各個方面的不足,前端、后端、運維、DBA、架構(gòu)....等等維度都有可能出現(xiàn)問題,需要不同的方案和不同的技術(shù)來優(yōu)化,不要想著一勞永逸。
OK,可能有點扯遠(yuǎn)了,還是說回到連接池,下一篇文章會介紹阿里的Druid連接池技術(shù)并將其整合到項目中,待續(xù)。