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