歡迎您光臨本站 註冊首頁

hibernate大數據性能處理

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
在項目中使用Hibernate進行大數據量的性能測試,有一些總結,
1) 在處理大數據量時,會有大量的數據緩衝保存在Session的一級緩存中,這緩存大太時會嚴重顯示性能,所以在使用Hibernate處理大數據量的,可以使用session.clear()或者session. Evict(Object) 在處理過程中,清除全部的緩存或者清除某個對象.
2) 對大數據量查詢時,慎用list()或者iterator()返回查詢結果,
1. 使用List()返回結果時,Hibernate會所有查詢結果初始化為持久化對象,結果集較大時,會佔用很多的處理時間.
2. 而使用iterator()返回結果時,在每次調用iterator.next()返回對象並使用對象時,Hibernate才調用查詢將對應的對象初始化,對於大數據量時,每調用一次查詢都會花費較多的時間.當結果集較大,但是含有較大量相同的數據,或者結果集不是全部都會使用時,使用iterator()才有優勢.
3. 對於大數據量,使用qry.scroll()可以得到較好的處理速度以及性能.直接對結果集向前向後滾動.
3) 對於關聯操作,Hibernate雖然可以表達複雜的數據關係,但請慎用,使數據關係較為簡單時會得到較好的效率,特別是較深層次的關聯時,性能會很差.
4) 對含有關聯的PO(持久化對象)時,若default-cascade="all"或者 「save-update」,新增PO時,請注意對PO中的集合的賦值操作,有可能是的多執行一次update操作.
5) 在一對多、多對一的關係中,使用延遲載入機制,會使不少的對象在使用時方會初始化,這樣可是的節省內存空間以及減少的負荷,若PO中的集合沒有被使用時,就可減少互資料庫的交互從而減少處理時間. 資料庫
什麼叫n 1次select查詢問題?
在Session的緩存中存放的是相互關聯的對象圖.默認情況下,當Hibernate從資料庫中載入Customer對象時,會同時載入所有關聯的Order對象.以Customer和Order類為例,假定ORDERS表的CUSTOMER_ID外鍵允許為null,圖1列出了CUSTOMERS表和ORDERS表中的記錄.
以下Session的find()方法用於到資料庫中檢索所有的Customer對象:
List customerLists=session.find("from Customer as c");
運行以上find()方法時,Hibernate將先查詢CUSTOMERS表中所有的記錄,然後根據每條記錄的ID,到ORDERS表中查詢有參照關係的記錄,Hibernate將依次執行以下select語句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
通過以上5條select語句,Hibernate載入了4個Customer對象和5個Order對象,在內存中形成了一幅關聯的對象圖,參見圖2.


Hibernate在檢索與Customer關聯的Order對象時,使用了默認的立即檢索策略.這種檢索策略存在兩大不足:
(a) select語句的數目太多,需要頻繁的訪問資料庫,會影響檢索性能.如果需要查詢n個Customer對象,那麼必須執行n 1次select查詢語句.這就是經典的n 1次select查詢問題.這種檢索策略沒有利用SQL的連接查詢功能,例如以上5條select語句完全可以通過以下1條select語句來完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select語句使用了SQL的左外連接查詢功能,能夠在一條select語句中查詢出CUSTOMERS表的所有記錄,以及匹配的ORDERS表的記錄.
(b)在應用邏輯只需要訪問Customer對象,而不需要訪問Order對象的場合,載入Order對象完全是多餘的操作,這些多餘的Order對象白白浪費了許多內存空間.
為了解決以上問題,Hibernate提供了其他兩種檢索策略:延遲檢索策略和迫切左外連接檢索策略.延遲檢索策略能避免多餘載入應用程序不需要訪問的關聯對象,迫切左外連接檢索策略則充分利用了SQL的外連接查詢功能,能夠減少select語句的數目.
剛查閱了hibernate3的文檔:
查詢抓取(默認的)在N 1查詢的情況下是極其脆弱的,因此我們可能會要求在映射文檔中定義使用連接抓取:
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set
<many-to-one name="mother" class="Cat" fetch="join"/>
在映射文檔中定義的抓取策略將會有產生以下影響:
通過get()或load()方法取得數據.
只有在關聯之間進行導航時,才會隱式的取得數據(延遲抓取).
條件查詢
在映射文檔中顯式的聲明 連接抓取做為抓取策略並不會影響到隨後的HQL查詢.
通常情況下,我們並不使用映射文檔進行抓取策略的定製.更多的是,保持其默認值,然後在特定的事務中, 使用HQL的左連接抓取(left join fetch) 對其進行重載.這將通知 Hibernate在第一次查詢中使用外部關聯(outer join),直接得到其關聯數據. 在條件查詢 API中,應該調用 setFetchMode(FetchMode.JOIN)語句.
6) 對於大數據量新增、修改、刪除操作或者是對大數據量的查詢,與資料庫的交互次數是決定處理時間的最重要因素,減少交互的次數是提升效率的最好途徑,所以在開發過程中,請將show_sql設置為true,深入了解Hibernate的處理過程,嘗試不同的方式,可以是的效率提升.
7) Hibernate是以JDBC為基礎,但是Hibernate是對JDBC的優化,其中使用Hibernate的緩衝機制會使性能提升,如使用二級緩存以及查詢緩存,若命中率較高明,性能會是到大幅提升.


8) Hibernate可以通過設置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等屬性,對Hibernate進行優化.
hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25
這兩個選項非常非常非常重要!!!將嚴重影響Hibernate的CRUD性能!
C = create, R = read, U = update, D = delete
Fetch Size 是設定JDBC的Statement讀取數據的時候每次從資料庫中取出的記錄條數.
例如一次查詢1萬條記錄,對於Oracle的JDBC驅動來說,是不會1次性把1萬條取出來的,而只會取出Fetch Size條數,當紀錄集遍歷完了這些記錄以後,再去資料庫取Fetch Size條數據.
因此大大節省了無謂的內存消耗.當然Fetch Size設的越大,讀資料庫的次數越少,速度越快;Fetch Size越小,讀資料庫的次數越多,速度越慢.
這有點像平時我們寫程序寫硬碟文件一樣,設立一個Buffer,每次寫入Buffer,等Buffer滿了以後,一次寫入硬碟,道理相同.
Oracle資料庫的JDBC驅動默認的Fetch Size=10,是一個非常保守的設定,根據我的測試,當Fetch Size=50的時候,性能會提升1倍之多,當Fetch Size=100,性能還能繼續提升20%,Fetch Size繼續增大,性能提升的就不顯著了.
因此我建議使用Oracle的一定要將Fetch Size設到50.
不過並不是所有的資料庫都支持Fetch Size特性,例如MySQL就不支持.
MySQL就像我上面說的那種最壞的情況,他總是一下就把1萬條記錄完全取出來,內存消耗會非常非常驚人!這個情況就沒有什麼好辦法了 :(
Batch Size是設定對資料庫進行批量刪除,批量更新和批量插入的時候的批次大小,有點相當於設置Buffer緩衝區大小的意思.
Batch Size越大,批量操作的向資料庫發送sql的次數越少,速度就越快.我做的一個測試結果是當Batch Size=0的時候,使用Hibernate對Oracle資料庫刪除1萬條記錄需要25秒,Batch Size = 50的時候,刪除僅僅需要5秒!!!
//
我們通常不會直接操作一個對象的標識符(identifier),因此標識符的setter方法應該被聲明為私有的(private).這樣當一個對象被保存的時候,只有Hibernate可以為它分配標識符.你會發現Hibernate可以直接訪問被聲明為public,private和protected等不同級別訪問控制的方法(accessor method)和欄位(field). 所以選擇哪種方式來訪問屬性是完全取決於你,你可以使你的選擇與你的程序設計相吻合.
所有的持久類(persistent classes)都要求有無參的構造器(no-argument constructor);Hibernate必須要使用Java反射機制(Reflection)來實例化對象.構造器(constructor)的訪問控制可以是私有的(private),然而當生成運行時代理(runtime proxy)的時候將要求使用至少是package級別的訪問控制,這樣在沒有位元組碼編入(bytecode instrumentation)的情況下,從持久化類里獲取數據會更有效率一些.



hibernate.max_fetch_depth 設置外連接抓取樹的最大深度
取值. 建議設置為0到3之間
就是每次你在查詢時,會級聯查詢的深度,譬如你對關聯vo設置了eager的話,如果fetch_depth值太小的話,會發多很多條sql
Hibernate的Reference之後,可以採用批量處理的方法,當插入的數據超過10000時,就flush session並且clear.
下面是一個測試method.
1 /** */ /**
2 * 測試成批插入數據的事務處理,返回是否成功
3 *
4 * @param objPO Object
5 * @return boolean
6 */
7 public boolean insertBatch( final Object objPO) {
8 boolean isSuccess = false ;
9 Transaction transaction = null ;
10 Session session = openSession();
11 try {
12 transaction = session.beginTransaction();
13 for ( int i = 0 ; i < 100000 ; i ) {
14 session.save(objPO);
15 if (i % 50 == 0 ) {
16 // flush a batch of inserts and release memory
17 session.flush();
18 session.clear();
19 }
20 }
21 transaction.commit();
22 logger.info( " transaction.wasCommitted: "
23 transaction.wasCommitted());
24 isSuccess = true ;
25 } catch (HibernateException ex) {
26 if (transaction != null ) {
27 try {
28 transaction.rollback();
29 logger.error( " transaction.wasRolledBack: "
30 transaction.wasRolledBack());
31 } catch (HibernateException ex1) {
32 logger.error(ex1.getMessage());
33 ex1.printStackTrace();
34 }
35 }
36 logger.error( " Insert Batch PO Error: " ex.getMessage());
37 ex.printStackTrace();
38 } finally {
39 if (transaction != null ) {
40 transaction = null ;
41 }
42 session.close();
43 }
44 return isSuccess;
45 }
46
這只是簡單的測試,實際項目中遇到的問題,要比這個複雜得多.
這時候,我們可以讓Spring來控制Transaction,自己來控制Hibernate的Session,隨時更新數據.
,利用HibernateDaoSupport類來自定義個方法打開Session;
1public Session openSession(){
2
3 return getHibernateTemplate().getSessionFactory().openSession();
4
5 }


然後,用打開的Session處理你的數據;
1protected void doBusiness(Session session) {
2
3 while (true) {
4 //do your business with the opening session
5 someMethod(session);
6 session.flush();
7 session.clear();
8 logger.info("good job!");
9 }
10}
每做一次數據操作,就更新一次Session,這樣可以保證每次數據操作都成功,否則就讓Spring去控制它roll back吧.
,記得關閉Session.
1 Session session = openSession();
2 doBusiness(session);
3 session.close(); // 關閉session


[火星人 ] hibernate大數據性能處理已經有1797次圍觀

http://coctec.com/docs/java/show-post-60095.html