Hibernate 性能之?dāng)?shù)據(jù)庫(kù)連接池
1. 前言
從本節(jié)課程開始,和大家一起聊聊 Hibernate 中的性能問題,面對(duì)開發(fā)者,Hibernate 表現(xiàn)出卓越的數(shù)據(jù)庫(kù)操作能力。
使用框架最大的優(yōu)勢(shì)就是帶來操作的快捷、便利。同時(shí),因?yàn)榭蚣艿姆庋b性,其性能往往比原生開發(fā)要慢。所以了解、掌握 Hibernate 的性能調(diào)優(yōu)方案是提升性能的不二法則。
了解其性能優(yōu)化方案,編寫最好的性能優(yōu)化策略,對(duì)每一個(gè)開發(fā)者而言,都是一個(gè)必選題。通過本節(jié)課程的學(xué)習(xí),你將了解到:
- 什么是數(shù)據(jù)庫(kù)連接池;
- HIbernate 中如何使用數(shù)據(jù)庫(kù)連接池。
2. 數(shù)據(jù)庫(kù)連接池
面對(duì)大量的數(shù)據(jù)庫(kù)操作請(qǐng)求,數(shù)據(jù)庫(kù)連接池能很好地幫助 Hibernate 避開網(wǎng)絡(luò)開銷所產(chǎn)生的性能消耗。
什么是數(shù)據(jù)庫(kù)連接池?
一般講池子是用來養(yǎng)魚的,但數(shù)據(jù)庫(kù)連接池不是養(yǎng)魚的,而是養(yǎng)了好多的 Connection 對(duì)象。
當(dāng)應(yīng)用程序需要一個(gè)連接對(duì)象時(shí),便向連接池租用一個(gè)。用完后,再返回給連接池,這樣連接池中的連接對(duì)象便可以反復(fù)使用,達(dá)到重用連接對(duì)象的目的。
Connection 的功能本質(zhì)是通過網(wǎng)絡(luò) API 完成進(jìn)程和進(jìn)程之間的遠(yuǎn)程連接,每一次連接的性能消耗都是很大的。
如果每一次需要時(shí)都重開一個(gè)連接,用完后便立馬銷毀,其代價(jià)是非常大的,如果使用連接池便可以減少這種性能消耗。
Hibernate 本身沒有提供較佳的數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn),其實(shí)也沒有必要重新造輪子。因?yàn)橛行袠I(yè)認(rèn)可的、穩(wěn)定可靠的第三方數(shù)據(jù)庫(kù)連接池可用。
如:
- DBCP;
- C3P0;
- Proxool。
幾位都是久經(jīng)沙場(chǎng)考驗(yàn)、絕對(duì)忠誠(chéng)可靠的老同志。
因?yàn)?Hibernate 3.0 后的版本不再支持 DBCP 數(shù)據(jù)庫(kù)連接池,DBPC 在此略過不提。但是,不能質(zhì)疑 DBCP 在行業(yè)內(nèi)的領(lǐng)導(dǎo)性。
本節(jié)課就和大家一起講解在 Hibernate 中使用數(shù)據(jù)庫(kù)連接池,讓其 Hibernate 的起飛姿勢(shì)更優(yōu)雅。
2.1 C3P0
- 添加 C3P0 依賴包:
從官網(wǎng)下載的 Hibernate 框架包中已經(jīng)包含所有的 C3P0 依賴包,可直接添加進(jìn)去:
- c3p0-0.9.1.jar;
- hibernate-c3p0-4.2.0.Final.jar。
- 在 Hibernate 的主配置文件中添加如下屬性后,便能自動(dòng)啟動(dòng) C3P0 連接池的功能。
<property name="connection.url">jdbc:mysql://localhost:3306/myhibernate?useSSL=false</property>
<property name="connection.username">root</property>
<property name="connection.password">abc123</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.pool_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">120</property>
<property name="hibernate.c3p0.testConnectionOnCheckout">true</property>
Hibernate 會(huì)把產(chǎn)生連接對(duì)象的重?fù)?dān)交給 C3P0 。
連接池對(duì)象本身會(huì)有一些常用的屬性,在后面我們?cè)僭敿?xì)講解。
2.2 Proxool
- 添加依賴包:
- hibernate-proxool-4.2.0.Final.jar;
- proxool-0.8.3.jar。
Proxool 是 Hibernate 官方指定的數(shù)據(jù)庫(kù)連接池,在 Hibernate 的框架包中就包含的有,很容易找到。
- 在 Hibernate 的主配置文件中添加:
<property name="hibernate.proxool.pool_alias">ProxoolPool</property>
<property name="hibernate.proxool.xml">proxool.xml</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.ProxoolConnectionProvider</property>
<property name="hibernate.proxool.existing_pool">true</property>
proxool 的配置和 C3P0 的配置有點(diǎn)不一樣,在主配置文件中告訴 Hibernate 使用 proxool ,但是與 proxool 相關(guān)的更多配置信息需要放到特別指定的 proxool.xml 文件中。
所以,需要單獨(dú)創(chuàng)建一個(gè)名為 proxool.xml 的文件,保存在和主配置文件相同的目錄下。其內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<something-else-entirely>
<proxool>
<alias>ProxoolPool</alias>
<driver-url>jdbc:mysql://localhost:3306/myhibernate?useSSL=false</driver-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver-properties>
<property name="user" value="root" />
<property name="password" value="abc123" />
</driver-properties>
<!--最大連接數(shù)(默認(rèn)5個(gè)),超過了這個(gè)連接數(shù),再有請(qǐng)求時(shí),就排在隊(duì)列中等候,最大的等待請(qǐng)求數(shù)由maximum-new-connections決定 -->
<maximum-connection-count>100</maximum-connection-count>
<!--最小連接數(shù) -->
<minimum-connection-count>10</minimum-connection-count>
<!--proxool 自動(dòng)偵察各個(gè)連接狀態(tài)的時(shí)間間隔(毫秒),偵察到空閑的連接就馬上回收,超時(shí)的銷毀默認(rèn)30秒 -->
<house-keeping-sleep-time>90000</house-keeping-sleep-time>
<!--沒有空閑連接時(shí),可以分配在隊(duì)列中等候的最大請(qǐng)求數(shù)-->
<maximum-new-connections>10</maximum-new-connections>
<!--最少保持的空閑連接數(shù)(默認(rèn)2個(gè)) -->
<prototype-count>5</prototype-count>
<!--在使用之前進(jìn)行測(cè)試 -->
<test-before-use>true</test-before-use>
<!--用于保持連接的測(cè)試語句 -->
<house-keeping-test-sql>select CURRENT_DATE</house-keeping-test-sql>
</proxool>
</something-else-entirely>
Hibernate 4.x 的低版本中使用 proxool 時(shí)存在一個(gè)沒有解決的 bug 。
會(huì)拋出 “The url cannot be null” 異常,建議使用 proxool 時(shí),切換高版本 Hibernate 。
當(dāng)然,除了使用前面介紹的 c3p0 和 proxool,Hiberante 還可以通過 JNDI 的方式連接到特定服務(wù)器中提供的數(shù)據(jù)庫(kù)連接池,有興趣的話,可以自己研究研究。
3. 數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)原理
在程序的世界,有一個(gè)緩存概念,數(shù)據(jù)庫(kù)連接池也可以看成是一個(gè)緩存池。
企業(yè)級(jí)的數(shù)據(jù)庫(kù)連接池除了要考慮其復(fù)用以外,還要考慮并發(fā)、性能等諸多因素。實(shí)現(xiàn)一個(gè)完備的、被行業(yè)認(rèn)定的數(shù)據(jù)庫(kù)連接池并不是一件簡(jiǎn)單輕松的事情。
但如果只是討論數(shù)據(jù)庫(kù)連接池的基本原理,了解其實(shí)現(xiàn)過程,倒也不難。
數(shù)據(jù)庫(kù)連接池主要解決以下 2 個(gè)方面的問題:
- 不要影響或改變用戶使用 connection 連接對(duì)象的標(biāo)準(zhǔn)流程。如創(chuàng)建、關(guān)閉等正常操作,但是用戶在創(chuàng)建或需要連接對(duì)象時(shí),不是直接創(chuàng)建,而是從池子里面尋找一個(gè)可用的連接對(duì)象;
- 用戶使用完連接對(duì)象后,在關(guān)閉連接對(duì)象時(shí),不是真正關(guān)閉,而是返回給連接池。
對(duì)于連接池本身,需要考慮的問題有:
- 一個(gè)應(yīng)用程序中,一般只需要一個(gè)連接池對(duì)象,并保證在整個(gè)應(yīng)用程序中都能訪問。所以,連接池對(duì)象本身是基于單例設(shè)計(jì)模式;
- 實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池時(shí),都會(huì)有一些基本的參數(shù)設(shè)置:
public class ConnectionPool implements DataSource {
// 最大連接數(shù),一般設(shè)置為0表示沒有限制
private int maxActive;
// 最大空閑連接數(shù) ,設(shè) 0 表示沒有限制
private int maxIdle;
//連接池中最小空閑連接數(shù)
private int minIdle;
// 初始化連接數(shù)目
private int initialSize;
// 新的請(qǐng)求等待時(shí)間
private int maxWait;
//從沒有正確關(guān)閉連接的程序中恢復(fù)數(shù)據(jù)庫(kù)的連接
private boolean removeAbandoned;
// 活動(dòng)連接的最大空閑時(shí)間,單位為秒
private int removeAbandonedTimeout;
// 連接池中連接可空閑的時(shí)間,單位為毫秒 針對(duì)連接池中的連接對(duì)象
private int minEvictableIdleTimeMillis;
private int timeBetweenEvictionRunsMillis;
}
數(shù)據(jù)庫(kù)連接池中有一個(gè)比較重要的方法,為用戶提供連接對(duì)象。此方法會(huì)使用代理設(shè)計(jì)模式,為用戶提供一個(gè)代理對(duì)象,既不影響用戶的正常使用,又能在用戶使用期間進(jìn)行代碼注入。
如下面代碼所示:
@Override
public Connection getConnection() throws SQLException {
return null;
}
探討數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)是一個(gè)高級(jí)的問題,即使是編寫一個(gè)比較簡(jiǎn)單的連接池對(duì)象也將涉及到單例設(shè)計(jì)模式和代理設(shè)計(jì)模式,也需要?jiǎng)?chuàng)建動(dòng)態(tài)代理對(duì)象的相關(guān)知識(shí)。
本節(jié)課還是偏向于從應(yīng)用層面講解 Hibernate 中如何使用數(shù)據(jù)庫(kù)連接池,更多與連接池有關(guān)的深層次內(nèi)容,有興趣者可查閱相關(guān)資料。
4. 小結(jié)
結(jié)束往往意味著是一個(gè)新的開始。
本節(jié)課程和大家講解了數(shù)據(jù)庫(kù)連接池相關(guān)的概念,但是,數(shù)據(jù)庫(kù)連接池的核心理論知識(shí)觸及的還遠(yuǎn)遠(yuǎn)不夠。
本課程通過講解 Hibernate 使用數(shù)據(jù)庫(kù)連接池, 就當(dāng)是拋磚引玉,這里起一個(gè)頭,大家有興趣可以自己研究、探討。