程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> hibernate批量處理大數據,hibernate

hibernate批量處理大數據,hibernate

編輯:JAVA綜合教程

hibernate批量處理大數據,hibernate


spring在管理hibernate上有獨到的地方可以順手拿來用,我也是想在能不拋棄hibernate的基礎上盡可能多挖掘一下它的一些性能提升上的做法,總結大家的看法,基本得出一致結論:復雜查詢依靠jdbc的sql或者hibernate提供的本地化sql封裝,或者使用spring的管理,都可以提升性能和效率.我認為對於hibernate的性能優化應該是無止境的.

在項目中使用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; View Code

通過以上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。
  View Code

這只是簡單的測試,實際項目中遇到的問題,要比這個復雜得多。 

這時候,我們可以讓Spring來控制Transaction,自己來控制Hibernate的Session,隨時更新數據。 
首先,利用HibernateDaoSupport類來自定義個方法打開Session;
  View Code

然後,用打開的Session處理你的數據;

  View Code

每做一次數據操作,就更新一次Session,這樣可以保證每次數據操作都成功,否則就讓Spring去控制它roll back吧。 

最後,記得關閉Session。
1  Session session  =  openSession(); 
2 doBusiness(session); 
3 session.close();  // 關閉session

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved