hibernate的一些優化策略
阿新 • • 發佈:2019-01-28
Robbin總結的Hibernate效能優化要點(一):
1.儘量使用many-to-one,避免使用單項one-to-many
2.靈活使用單向one-to-many
3.不用一對一,使用多對一代替一對一
4.配置物件快取,不使用集合快取
5.一對多使用Bag 多對一使用Set
6.繼承使用顯示多型 HQL:from object polymorphism="exlicit" 避免查處所有物件
7.消除大表,使用二級快取
對於上面這些,Robbin進行了詳細的講解。
==========================
one-to-many:
使用inverse=false(default),物件的關聯關係是由parent物件來維護的
而inverse=true的情況下,一般使用者雙向多對多關聯,由子物件維護關聯關係,增加子物件的時候需要顯示:child.setParent(child)
為了提高效能,應該儘量使用雙向one-to-many inverse=true,在MVC結構中的DAO介面中應該直接用Session持久化物件,避免通過關聯關係(這句話有點不理解),而在單項關係中正確使用二級快取,則可以大幅提高以查詢為主的應用。
多對一效能問題比較少,但是要避免經典N+1問題。
通過主鍵進行關聯,相當於大表拆分小表。(這個是區分面向物件設計和麵向過程設計的一個關鍵點)
===============================
list、bag、set的正確運用
one-to-many:
A、使用list 需要維護Index Column欄位,不能被用於雙向關聯,而且必須使用inverse=false,需要謹慎使用在某些稀有場合(基本上是不予考慮使用)
B、bag/set在one-to-many中語義基本相同,推薦使用bag
many-to-one:
A、bag和set不同,bag允許重複插入,建議使用set
=========================
在龐大的集合分頁中應該使用session.createFilter
session.createFilter(parent.getChildren(),""),setFirstResult(0),setMaxResult(10))
===================
避免N+1 參考(http://www.javaeye.com/post/266972)
在多對一的情況下,查詢child物件,當在頁面上顯示每個子類的父類物件的時候會導致N+1次查詢,需要採用下面的方法避免:many-to-one fetch="join|select"(該方法可能有問題)
=========================
inverse=true 無法維護集合快取(還不是很理解集合快取和物件快取)
OLTP型別的web應用,可以群集水平擴充套件,不可避免的出現數據庫瓶頸
框架能降低訪問資料庫的壓力,採用快取是衡量一個框架是否優秀的重要標準,從快取方面看Hibernate
A、物件快取,細顆粒度,是針對表的級別,透明化訪問,因為有不改變程式碼的好處,所以是ORM提高效能的法寶
B、Hibernate是目前ORM框架中快取效能最好的框架
C、查詢快取
===============================
最後Robbin還針對大家經常出現的Hibernate vs iBatis的討論進行了一個總結:
對於OLTP應用,使用ORM框架 而OLEB應用(不確定是什麼應用)最好採用JDBC或者其他方法處理
Hibernate傾向於細顆粒度設計,面向物件,將大表拆分為多個小表,消除冗餘欄位,通過二級快取提升效能。
iBatis傾向於粗顆粒度設計,面向關係,儘量把表合併,通過Column冗餘,消除關聯關係,但是iBatis沒有有效的快取手段。
可以說Robbin的效能總結對於使用Hibernate的開發人員有著很重要的點撥作用。非常感謝他無私奉獻自己的經驗。
有很多人認為Hibernate天生效率比較低,確實,在普遍情況下,需要將執行轉換為SQL語句的Hibernate的效率低於直接JDBC存取,然 而,在經過比較好的效能優化之後,Hibernate的效能還是讓人相當滿意的,特別是應用二級快取之後,甚至可以獲得比較不使用快取的JDBC更好的效能,下面介紹一些通常的Hibernate的優化策略:
1.抓取優化
抓取是指Hibernate如何在關聯關係之間進行導航的時候,Hibernate如何獲取關聯物件的策略,其主要定義了兩個方面:如何抓取和何時抓取
1)如何抓取。
Hibernate3主要有兩種種抓取方式,分別應用於物件關聯例項(many-to-one、one-to-one)和物件關聯集合(set、map等),總共是四種變種
JOIN抓取: 通過在SELECT語句中使用OUTER JOIN來獲得物件的關聯例項或者關聯集合)
SELECT抓取: 另外發送一條SELECT語句來抓取當前物件的關聯實體和集合
在我的開發經歷中,此處對效能的優化是比較有限的,並不值得過多關注
例:
A.應用於物件關聯例項(預設是false)
<many-to-one name=".." outer-join="true/false/auto" .../>
B.應用於物件關聯集合(預設是auto)
<set name=".." fetch="join/select" ... >
....
</set>
2)何時抓取
主要分為延遲載入和立即抓取,預設的情況下Hibernate3對物件關聯實採用延遲載入,普通屬性採用立即抓取,通過延遲載入和採用適當的抓取粒度,與不採用優化相比往往可以將效能提升數倍
立即抓取:當抓取宿主物件時,同時抓取其關聯物件和關聯集以及屬性
延遲載入:當抓取宿主物件時,並不抓取其關聯物件,而是當對其物件進行呼叫時才載入
例:
A.應用於物件關聯例項(預設是延遲載入)
<many-to-one name=".." lazy="true/false" .../>
B.應用於物件關聯集合(預設是延遲載入)
<set name=".." lazy="true/false" ... >
....
</set>
對於延遲載入,需要注意的時,對延遲物件的使用必須在Session關閉之前進行,Hibernate的 LazyInitalizationException往往就是由於在Session的生命期外使用了延遲載入的物件。當我們進行Web開發時,可以使用 OpenSessionInView模式,當請求開始時開啟session,當請求響應結束時才關閉session,不過,在使用 OpenSessionInView模式時,需要注意如果響應時間比較長(業務比較複雜或者客戶端是低速網路),將Session資源(也就是資料庫的連 接)佔用太久的話可以會導致資源耗盡
3)抓取粒度
抓取粒度指的是物件在關聯關係之間被導航時一次預先載入的數量,Hibernate程式的效能比較差往往就在於沒有對抓取粒度仔細考慮,當載入一個列表並在列表中的每個物件中對其關聯進行導航時,往往導致N+1條SQL語句查詢。
例:
A.應用於物件關聯例項(預設為1),注意,對物件關聯例項的設定是在被關聯的物件之上的,譬如
class User
{
Group g;
}
那麼抓取粒度應該在Group的配置檔案之上,見下
<class name="Group" table="group" batch-size="..">
...
</class>
對該值並沒有一個約定俗成的值,根據情況而定,如果被關聯表資料比較少,則可以設定地小一些,3-20,如果比較大則可以設到30-50,注意的時候,並不是越多越好,當其值超過50之後,對效能並沒有多大改善但卻無謂地消耗記憶體
假設有如下例子:
List<User> users = query.list();
如果有20個User,並對這20個User及其Group進行遍歷,如果不設定batch-size(即batch-size="1"),則在最糟糕的情況
下,需要1 + 20條SQL語句,如果設定batch-size="10",則最好的情況下只需要1 + 2條SQL語句
B.應用於物件關聯集合(預設為1)
<set name=".." batch-size="" ... >
....
</set>
2.二級快取
Hibernate 對資料的快取包括兩個級:一級快取,在Session的級別上進行,主要是物件快取,以其id為鍵儲存物件,在Session的生命期間存在;二級快取, 在SessionFactory的級別上進行,有物件快取和查詢快取,查詢快取以查詢條件為鍵儲存查詢結果,在SessionFactory的生命期間存 在。預設地,Hibernate只啟用一級快取,通過正確地使用二級快取,往往可以獲得意想不到的效能。
1)物件快取:
當抓取一個物件之後,Hiberate將其以id為鍵快取起來,當下次碰到抓取id相同的物件時,可以使用如下配置
方法1:在快取物件上配置
<class ...>
<cache useage="read-only/write/...." regions="group" />
</class>
useage 表示使用什麼型別的快取,譬如只讀快取、讀寫快取等等(具體參見Hibernate參考指南),值得注意的時,有部分快取在Hibernate的實現中不 支援讀寫快取,譬如JBossCache在Hibernate的實現中只是一種只讀快取,具體快取實現對快取型別的支援情況,可以參見 org.hibernate.cache包
regions表示快取分塊,大部分的快取實現往往對快取進行分塊,該部分是可選的,詳細參見各快取實現
方法2:在hibernate.cfg.xml中配置
<cache class=".." useage=".." regions=".."/>
我認為第二種更好,可以統一管理
2)查詢快取
查詢時候將查詢結果以查詢條件為鍵儲存起來,需要配置如下
A.在hibernate.cfg.xml中配置(啟用查詢快取)
<property name="hibernate.cache.use_query_cache">true</property> (前面的屬性名可參見常量
org.hibernate.cfg.Enviroment.USE_QUERY_CACHE)
B.程式
query.setCacheable(true);
query.setCacheRegions(...);
需要注意的是,查詢快取與物件快取要結合更有效,因為查詢快取僅快取查詢結果列表的主鍵資料
一般情況下在開發中,對一些比較穩定而又被頻繁引用的資料,譬如資料字典之類的,將其進行二級快取,對一些查詢條件和查詢資料變化不頻繁而又常常被使用的 查詢,將其進行二級快取。由於二級快取是放在記憶體中,而且Hibernate的快取不是弱引用快取(WeekReference),所以注意不要將大塊的 資料放入其中,否則可能會被記憶體造成比較大的壓力。
3.批量資料操作
當進行大批量資料操作(幾萬甚至幾十幾百萬)時,需要注意兩點,一,批量提交,二,及時清除不需要的一級快取資料
1) 所謂的批量提交,就是不要頻繁使用session的flush,每一次進行flush,Hibernate將PO資料於資料庫進行同步,對於海量級資料操 作來說是效能災難(同時提交幾千條資料和提交一條資料flush一次效能差別可能會是幾十倍的差異)。一般將資料操作放在事務中,當事務提交時 Hibernate自動幫你進行flush操作。
2)及時清除不需要的一級快取資料:由於Hibernate預設採用一級快取,而在 session的生命期間,所有資料抓取之後會放入一級快取中,而當資料規模比較龐大時,抓取到記憶體中的資料會讓記憶體壓力非常大,一般分批操作資料,被一 次操作之後將一級快取清除,譬如
session.clear(User.class)
4.雜項
dynamic-insert,dynamic-update,動態插入和動態更新,指的是讓Hibernate插入資料時僅插入非空資料,當修改資料時只修改變化的資料,譬如對於
class User
{
id
username
password
}
如果u.id=1, u.username="ayufox",u.password=null,那麼如果不設定動態插入,則其sql語句是 insert into users(id, username, password) values (1, 'ayufox', '),如果設定則其 sql語句是insert into users(username) valeus('ayufox')
在如上的情況下,如果修改 u.password='11',那麼如果不設定動態更新,則sql語句為update users set username='ayufox', password='11' where id = 1,如果設定則為update user set password='11' where d = 1
設定是在class的對映檔案中,如下
<class name="User" table="users" dynamic=insert="true/false" dynamic-update="true/false" ...>
</class>
該設定對效能的提升比較有限
==============================================
Hibernate效能優化(2) left join user.messages msg
group by user.id,user.name
having count(msg)>=1
===================================================================
文章分為十三個小塊兒對Hibernate效能優化技巧進行總結性分析,分析如下:
一、在處理大資料量時,會有大量的資料緩衝儲存在Session的一級快取中,這快取大太時會嚴重顯示效能,所以在使用Hibernate處理大資料量的,可以使用session. clear()或者session. evict(Object) 在處理過程中,清除全部的快取或者清除某個物件。
二、對大資料量查詢時,慎用list()或者iterator()返回查詢結果,
<1>. 使用List()返回結果時,Hibernate會所有查詢結果初始化為持久化物件,結果集較
大時,會佔用很多的處理時間。
<2>. 而使用iterator()返回結果時,在每次呼叫iterator.next()返回物件並使用物件時,
Hibernate才呼叫查詢將對應的物件初始化,對於大資料量時,每呼叫一次查詢都會花
費較多的時間。當結果集較大,但是含有較大量相同的資料,或者結果集不是全部都會
使用時,使用iterator()才有優勢。
<3>. 對於大資料量,使用qry.scroll()可以得到較好的處理速度以及效能。而且直接對結
果集向前向後滾動。
三、對於關聯操作,Hibernate雖然可以表達複雜的資料關係,但請慎用,使資料關係較為
簡單時會得到較好的效率,特別是較深層次的關聯時,效能會很差。
四、對含有關聯的PO(持久化物件)時,若default-cascade="all"或者 “save-update”,新增PO時,請注意對PO中的集合的賦值操作,因為有可能使得多執行一次update操作。
五、在一對多、多對一的關係中,使用延遲載入機制,會使不少的物件在使用時才會初始化,這樣可使得節省記憶體空間以及減少資料庫的負荷,而且若PO中的集合沒有被使用時,就可減少互資料庫的互動從而減少處理時間。
六、對於大資料量新增、修改、刪除操作或者是對大資料量的查詢,與資料庫的互動次數是決定處理時間的最重要因素,減少互動的次數是提升效率的最好途徑,所以在開發過程中,請將show_sql設定為true,深入瞭解Hibernate的處理過程,嘗試不同的方式,可以使得效率提升。
七、Hibernate是以JDBC為基礎,但是Hibernate是對JDBC的優化,其中使用Hibernate的緩衝機制會使效能提升,如使用二級快取以及查詢快取,若命中率較高明,效能會是到大幅提升。
八、Hibernate可以通過設定hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等屬性,對Hibernate進行優化。
九、不過值得注意的是,一些資料庫提供的主鍵生成機制在效率上未必最佳,大量併發insert資料時可能會引起表之間的互鎖。資料庫提供的主鍵生成機制,往往是通過在一個內部表中儲存當前主鍵狀態(如對於自增型主鍵而言,此內部表中就維護著當前的最大值和遞增量),之後每次插入資料會讀取這個最大值,然後加上遞增量作為新記錄的主鍵,之後再把這個新的最大值更新回內部表中,這樣,一次Insert操作可能導致資料庫內部多次表讀寫操作,同時伴隨的還有資料的加鎖解鎖操作,這對效能產生了較大影響。因此,對於併發Insert要求較高的系統,推薦採用uuid.hex 作為主鍵生成機制。
十、Dynamic Update 如果選定,則生成Update SQL 時不包含未發生變動的欄位屬性,這樣可以在一定程度上提升SQL執行效能.Dynamic Insert 如果選定,則生成Insert SQL 時不包含未發生變動的欄位屬性,這樣可以在一定程度上提升SQL執行效能
十一、在編寫程式碼的時候請,對將POJO的getter/setter方法設定為public,如果設定為private,Hibernate將無法對屬性的存取進行優化,只能轉而採用傳統的反射機制進行操作,這將導致大量的效能開銷(特別是在1.4之前的Sun JDK版本以及IBM JDK中,反射所帶來的系統開銷相當可觀)。
十二、在one-to-many 關係中,將many 一方設為主動方(inverse=false)將有助效能的改善。
十三、由於多對多關聯的效能不佳(由於引入了中間表,一次讀取操作需要反覆數次查詢),因此在設計中應該避免大量使用。
以上便是對Hibernate效能優化技巧總結,希望能對您有所幫助。
========================================================
批量刪除和修改
Hibernate是面向物件的,如果要對資料進行修改要先載入物件後修改,這樣的操作對於修改單條資料是沒什麼問題的。如果要更新批量資料,就可能佔用大量系統記憶體、需要執行多條select/update語句導致系統性能嚴重下降。所以在需要批量更新資料的時候,最好不要用上述方法。那麼有什麼更好方法麼?那就是使用HQL語句。
HQL語句可以像用SQL那樣進行批量操作資料。使用Hql就可以一次更新多條資料,而不用每次只更新一條資料了。
如:
更新User
Session session=factory.openSession();
Transaction tx=null;
try {
tx=session.beginTransaction();
//批量更新User
String hql="update User u set u.age=20 where u.age<=18";
//批量刪除,String hql=”delete User u where u.age>25”;
Query query=session.createQuery(hql);
int result=query.executeUpdate();
tx.commit();
} catch (HibernateException e) {
if(tx!=null) tx.rollback();
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
session.close();
}
選擇確當的查詢方法
Hibernatne 查詢分為兩類:一類是得到單個物件,get()和load方法;另一類是得到結果集,list()和iterator()方法。
get()方法和load()方法的區別在於對二級快取的使用上。load()方法會使用二級快取,而get()方法在一級快取沒有找到的情況下會直接查詢資料庫,不會去二級快取中查詢。在使用中,對使用了二級快取的物件進行查詢時最好使用load()方法,以充分利用二級快取來提高檢索的效率。
list()方法和iterator()方法之間的區別可以從以下幾個方面來進行比較。
1、 執行的查詢不同
list()方法在執行時,是直接執行查詢結果所需要的查詢語句,而iterator()方法則是先執行得到物件ID的查詢,然後再根據每個ID值去取得所要查詢的物件。因此,對於list()方式的查詢通常只會執行一個SQL語句,而對於iterator()方法的查詢則可能需要執行N+1條SQL語句(N為結果集中的記錄數)。
iterator()方法只是可能執行N+1條資料,具體執行SQL語句的數量取決於快取的情況以及對結果集的訪問情況。
2、 快取的使用
list()方法只能使用二級快取中的查詢快取,而無法使用二級快取對單個物件的快取(但是會把查詢出的物件放入二級快取中)。所以,除非重複執行相同的查詢操作,否則無法利用快取的機制來提高查詢的效率。
iterator()方法則可以充分利用二級快取,在根據ID檢索物件的時候會首先到快取中查詢,只有在找不到的情況下才會執行相應的查詢語句。所以,快取中物件的存在與否會影響到SQL語句的執行數量。
3、 對於結果集的處理方法不同
list()方法會一次獲得所有的結果集物件,而且它會依據查詢的結果初始化所有的結果集物件。這在結果集非常大的時候必然會佔據非常多的記憶體,甚至會造成記憶體溢位情況的發生。
iterator()方法在執行時不會一次初始化所有的物件,而是根據對結果集的訪問情況來初始化物件。因此在訪問中可以控制快取中物件的數量,以避免佔用過多快取,導致記憶體溢位情況的發生。使用iterator()方法的另外一個好處是,如果只需要結果集中的部分記錄,那麼沒有被用到的結果物件根本不會被初始化。所以,對結果集的訪問情況也是呼叫iterator()方法時執行資料庫SQL語句多少的一個因素。
所以,在使用Query物件執行資料查詢時應該從以上幾個方面去考慮使用何種方法來執行資料庫的查詢操作。
=========百度文庫資料的hibernate優化======================
優化總結?
???????????
要想優化Hibernate,我們必須知道應該從什麼地方進行優化,從什麼地方入手。Hibernate的優化方向:資料庫設計、HQL優化、快取、主配置、延遲載入、方法選用、集合選用、事物控制、批量操作
第一點:資料庫設計?
???????前邊部落格介紹過?資料庫設計,今天我們還從這開始,表的設計就是建樓的基礎,如何讓基礎簡潔而結實這是最重要的。
優化策略:?1.???建索引?
2.???減少表之間的關聯?
3.???簡化查詢欄位,沒用的欄位不要,已經對返回結果的控制,儘量返回少量資料?4.???適當的冗餘資料,不過分最求高正規化.
第二點:HQL優化
第三點:快取?
執行機制:?
介於應用程式和物理資料來源之間,其作用是為了降低應用程式對物理資料來源訪問的頻數,從而提高執行效能。?
快取被廣泛應用的用於優化資料庫。當一些資料被從資料庫中讀取出來的時候,我們可以把它們放到快取裡。這樣我們可以再次使用的時候直接從快取中取出來,這樣我們的效率就提高了很多。?
控制範圍:?
一級快取是session物件的生命週期通常對應的一個數據庫事務或者一個應用事務,它是事務範圍內的快取
二級快取是一個可插拔的快取外掛,它是由SessionFactory負責管理。由於SessionFactory物件的生命週期和應用程式的整個過程對應,所以二級快取是進城範圍或者叢集範圍內的快取。用於初始化很少更改的資料、不重要的資料,不會併發訪問的資料。
Hibernate快取的一些問題和建議:hibernate的快取?
第四點:捉取策略?
1.??捉取優化:Hibernate在關聯關係之間進行導航,充分利用Hibernate提供的技術?
2.??如何捉取?
立即捉取:當捉取宿主物件時,同時捉取其相關物件和關聯集以及屬性?
延遲載入:當捉宿主物件時,並不捉取其關聯物件,而是當對其物件進行呼叫時才載入。?
3.??捉取粒度:設定捉取個數?
第五點:批量資料處理(修改和刪除)?
在Hibernate2中,如果需要對任何資料進行修改和刪除操作都需要先執行查詢操作,在得到資料後才進行修改和刪除。?
1.??不適用Hibernate?API而是直接使用JDBC??API來做原生態SQL語句進行查詢,這種方法比較好,相對來說較快。
2.??運用儲存過程?
3.??一定量範圍內可以使用hibernate?API,但是特大資料量不行。
第六點:結果集的使用:?
?????
結果集的使用:list()和iterator()區別?查詢方式:?
list只能利用查詢快取(但在交易系統中查詢快取作用不大),無法利用二級快取中的單個實體,但是list查出的物件會寫入二級快取,但它一般只生成較少的sql語句,很多情況就是一條。?
iterator則利用二級快取,對於一條查詢語句,它會先從資料庫中找到所有符合條件的記錄的ID,在通過ID去快取找,對於快取中沒有的記錄,在構造語句從資料庫查出,第一次的執行會產生N+1條SQL語句。?產生結果:?用list可能會溢位?
通過Iterator,配合快取管理API,在海量資料查詢中可以很好的解決記憶體問題。?綜合考慮:?
一般List會填充二級快取,卻不能利用二級快取,而Iterator可以讀二級快取,然而無法命中的話,效率很低效。一般處理方法,就是第一次查詢使用list,隨後使用iterator查詢。
1.儘量使用many-to-one,避免使用單項one-to-many
2.靈活使用單向one-to-many
3.不用一對一,使用多對一代替一對一
4.配置物件快取,不使用集合快取
5.一對多使用Bag 多對一使用Set
6.繼承使用顯示多型 HQL:from object polymorphism="exlicit" 避免查處所有物件
7.消除大表,使用二級快取
對於上面這些,Robbin進行了詳細的講解。
==========================
one-to-many:
使用inverse=false(default),物件的關聯關係是由parent物件來維護的
而inverse=true的情況下,一般使用者雙向多對多關聯,由子物件維護關聯關係,增加子物件的時候需要顯示:child.setParent(child)
為了提高效能,應該儘量使用雙向one-to-many inverse=true,在MVC結構中的DAO介面中應該直接用Session持久化物件,避免通過關聯關係(這句話有點不理解),而在單項關係中正確使用二級快取,則可以大幅提高以查詢為主的應用。
多對一效能問題比較少,但是要避免經典N+1問題。
通過主鍵進行關聯,相當於大表拆分小表。(這個是區分面向物件設計和麵向過程設計的一個關鍵點)
===============================
list、bag、set的正確運用
one-to-many:
A、使用list 需要維護Index Column欄位,不能被用於雙向關聯,而且必須使用inverse=false,需要謹慎使用在某些稀有場合(基本上是不予考慮使用)
B、bag/set在one-to-many中語義基本相同,推薦使用bag
many-to-one:
A、bag和set不同,bag允許重複插入,建議使用set
=========================
在龐大的集合分頁中應該使用session.createFilter
session.createFilter(parent.getChildren(),""),setFirstResult(0),setMaxResult(10))
===================
避免N+1 參考(http://www.javaeye.com/post/266972)
在多對一的情況下,查詢child物件,當在頁面上顯示每個子類的父類物件的時候會導致N+1次查詢,需要採用下面的方法避免:many-to-one fetch="join|select"(該方法可能有問題)
=========================
inverse=true 無法維護集合快取(還不是很理解集合快取和物件快取)
OLTP型別的web應用,可以群集水平擴充套件,不可避免的出現數據庫瓶頸
框架能降低訪問資料庫的壓力,採用快取是衡量一個框架是否優秀的重要標準,從快取方面看Hibernate
A、物件快取,細顆粒度,是針對表的級別,透明化訪問,因為有不改變程式碼的好處,所以是ORM提高效能的法寶
B、Hibernate是目前ORM框架中快取效能最好的框架
C、查詢快取
===============================
最後Robbin還針對大家經常出現的Hibernate vs iBatis的討論進行了一個總結:
對於OLTP應用,使用ORM框架 而OLEB應用(不確定是什麼應用)最好採用JDBC或者其他方法處理
Hibernate傾向於細顆粒度設計,面向物件,將大表拆分為多個小表,消除冗餘欄位,通過二級快取提升效能。
iBatis傾向於粗顆粒度設計,面向關係,儘量把表合併,通過Column冗餘,消除關聯關係,但是iBatis沒有有效的快取手段。
可以說Robbin的效能總結對於使用Hibernate的開發人員有著很重要的點撥作用。非常感謝他無私奉獻自己的經驗。
有很多人認為Hibernate天生效率比較低,確實,在普遍情況下,需要將執行轉換為SQL語句的Hibernate的效率低於直接JDBC存取,然 而,在經過比較好的效能優化之後,Hibernate的效能還是讓人相當滿意的,特別是應用二級快取之後,甚至可以獲得比較不使用快取的JDBC更好的效能,下面介紹一些通常的Hibernate的優化策略:
1.抓取優化
抓取是指Hibernate如何在關聯關係之間進行導航的時候,Hibernate如何獲取關聯物件的策略,其主要定義了兩個方面:如何抓取和何時抓取
1)如何抓取。
Hibernate3主要有兩種種抓取方式,分別應用於物件關聯例項(many-to-one、one-to-one)和物件關聯集合(set、map等),總共是四種變種
JOIN抓取: 通過在SELECT語句中使用OUTER JOIN來獲得物件的關聯例項或者關聯集合)
SELECT抓取: 另外發送一條SELECT語句來抓取當前物件的關聯實體和集合
在我的開發經歷中,此處對效能的優化是比較有限的,並不值得過多關注
例:
A.應用於物件關聯例項(預設是false)
<many-to-one name=".." outer-join="true/false/auto" .../>
B.應用於物件關聯集合(預設是auto)
<set name=".." fetch="join/select" ... >
....
</set>
2)何時抓取
主要分為延遲載入和立即抓取,預設的情況下Hibernate3對物件關聯實採用延遲載入,普通屬性採用立即抓取,通過延遲載入和採用適當的抓取粒度,與不採用優化相比往往可以將效能提升數倍
立即抓取:當抓取宿主物件時,同時抓取其關聯物件和關聯集以及屬性
延遲載入:當抓取宿主物件時,並不抓取其關聯物件,而是當對其物件進行呼叫時才載入
例:
A.應用於物件關聯例項(預設是延遲載入)
<many-to-one name=".." lazy="true/false" .../>
B.應用於物件關聯集合(預設是延遲載入)
<set name=".." lazy="true/false" ... >
....
</set>
對於延遲載入,需要注意的時,對延遲物件的使用必須在Session關閉之前進行,Hibernate的 LazyInitalizationException往往就是由於在Session的生命期外使用了延遲載入的物件。當我們進行Web開發時,可以使用 OpenSessionInView模式,當請求開始時開啟session,當請求響應結束時才關閉session,不過,在使用 OpenSessionInView模式時,需要注意如果響應時間比較長(業務比較複雜或者客戶端是低速網路),將Session資源(也就是資料庫的連 接)佔用太久的話可以會導致資源耗盡
3)抓取粒度
抓取粒度指的是物件在關聯關係之間被導航時一次預先載入的數量,Hibernate程式的效能比較差往往就在於沒有對抓取粒度仔細考慮,當載入一個列表並在列表中的每個物件中對其關聯進行導航時,往往導致N+1條SQL語句查詢。
例:
A.應用於物件關聯例項(預設為1),注意,對物件關聯例項的設定是在被關聯的物件之上的,譬如
class User
{
Group g;
}
那麼抓取粒度應該在Group的配置檔案之上,見下
<class name="Group" table="group" batch-size="..">
...
</class>
對該值並沒有一個約定俗成的值,根據情況而定,如果被關聯表資料比較少,則可以設定地小一些,3-20,如果比較大則可以設到30-50,注意的時候,並不是越多越好,當其值超過50之後,對效能並沒有多大改善但卻無謂地消耗記憶體
假設有如下例子:
List<User> users = query.list();
如果有20個User,並對這20個User及其Group進行遍歷,如果不設定batch-size(即batch-size="1"),則在最糟糕的情況
下,需要1 + 20條SQL語句,如果設定batch-size="10",則最好的情況下只需要1 + 2條SQL語句
B.應用於物件關聯集合(預設為1)
<set name=".." batch-size="" ... >
....
</set>
2.二級快取
Hibernate 對資料的快取包括兩個級:一級快取,在Session的級別上進行,主要是物件快取,以其id為鍵儲存物件,在Session的生命期間存在;二級快取, 在SessionFactory的級別上進行,有物件快取和查詢快取,查詢快取以查詢條件為鍵儲存查詢結果,在SessionFactory的生命期間存 在。預設地,Hibernate只啟用一級快取,通過正確地使用二級快取,往往可以獲得意想不到的效能。
1)物件快取:
當抓取一個物件之後,Hiberate將其以id為鍵快取起來,當下次碰到抓取id相同的物件時,可以使用如下配置
方法1:在快取物件上配置
<class ...>
<cache useage="read-only/write/...." regions="group" />
</class>
useage 表示使用什麼型別的快取,譬如只讀快取、讀寫快取等等(具體參見Hibernate參考指南),值得注意的時,有部分快取在Hibernate的實現中不 支援讀寫快取,譬如JBossCache在Hibernate的實現中只是一種只讀快取,具體快取實現對快取型別的支援情況,可以參見 org.hibernate.cache包
regions表示快取分塊,大部分的快取實現往往對快取進行分塊,該部分是可選的,詳細參見各快取實現
方法2:在hibernate.cfg.xml中配置
<cache class=".." useage=".." regions=".."/>
我認為第二種更好,可以統一管理
2)查詢快取
查詢時候將查詢結果以查詢條件為鍵儲存起來,需要配置如下
A.在hibernate.cfg.xml中配置(啟用查詢快取)
<property name="hibernate.cache.use_query_cache">true</property> (前面的屬性名可參見常量
org.hibernate.cfg.Enviroment.USE_QUERY_CACHE)
B.程式
query.setCacheable(true);
query.setCacheRegions(...);
需要注意的是,查詢快取與物件快取要結合更有效,因為查詢快取僅快取查詢結果列表的主鍵資料
一般情況下在開發中,對一些比較穩定而又被頻繁引用的資料,譬如資料字典之類的,將其進行二級快取,對一些查詢條件和查詢資料變化不頻繁而又常常被使用的 查詢,將其進行二級快取。由於二級快取是放在記憶體中,而且Hibernate的快取不是弱引用快取(WeekReference),所以注意不要將大塊的 資料放入其中,否則可能會被記憶體造成比較大的壓力。
3.批量資料操作
當進行大批量資料操作(幾萬甚至幾十幾百萬)時,需要注意兩點,一,批量提交,二,及時清除不需要的一級快取資料
1) 所謂的批量提交,就是不要頻繁使用session的flush,每一次進行flush,Hibernate將PO資料於資料庫進行同步,對於海量級資料操 作來說是效能災難(同時提交幾千條資料和提交一條資料flush一次效能差別可能會是幾十倍的差異)。一般將資料操作放在事務中,當事務提交時 Hibernate自動幫你進行flush操作。
2)及時清除不需要的一級快取資料:由於Hibernate預設採用一級快取,而在 session的生命期間,所有資料抓取之後會放入一級快取中,而當資料規模比較龐大時,抓取到記憶體中的資料會讓記憶體壓力非常大,一般分批操作資料,被一 次操作之後將一級快取清除,譬如
session.clear(User.class)
4.雜項
dynamic-insert,dynamic-update,動態插入和動態更新,指的是讓Hibernate插入資料時僅插入非空資料,當修改資料時只修改變化的資料,譬如對於
class User
{
id
username
password
}
如果u.id=1, u.username="ayufox",u.password=null,那麼如果不設定動態插入,則其sql語句是 insert into users(id, username, password) values (1, 'ayufox', '),如果設定則其 sql語句是insert into users(username) valeus('ayufox')
在如上的情況下,如果修改 u.password='11',那麼如果不設定動態更新,則sql語句為update users set username='ayufox', password='11' where id = 1,如果設定則為update user set password='11' where d = 1
設定是在class的對映檔案中,如下
<class name="User" table="users" dynamic=insert="true/false" dynamic-update="true/false" ...>
</class>
該設定對效能的提升比較有限
==============================================
Hibernate效能優化(2) left join user.messages msg
group by user.id,user.name
having count(msg)>=1
===================================================================
文章分為十三個小塊兒對Hibernate效能優化技巧進行總結性分析,分析如下:
一、在處理大資料量時,會有大量的資料緩衝儲存在Session的一級快取中,這快取大太時會嚴重顯示效能,所以在使用Hibernate處理大資料量的,可以使用session. clear()或者session. evict(Object) 在處理過程中,清除全部的快取或者清除某個物件。
二、對大資料量查詢時,慎用list()或者iterator()返回查詢結果,
<1>. 使用List()返回結果時,Hibernate會所有查詢結果初始化為持久化物件,結果集較
大時,會佔用很多的處理時間。
<2>. 而使用iterator()返回結果時,在每次呼叫iterator.next()返回物件並使用物件時,
Hibernate才呼叫查詢將對應的物件初始化,對於大資料量時,每呼叫一次查詢都會花
費較多的時間。當結果集較大,但是含有較大量相同的資料,或者結果集不是全部都會
使用時,使用iterator()才有優勢。
<3>. 對於大資料量,使用qry.scroll()可以得到較好的處理速度以及效能。而且直接對結
果集向前向後滾動。
三、對於關聯操作,Hibernate雖然可以表達複雜的資料關係,但請慎用,使資料關係較為
簡單時會得到較好的效率,特別是較深層次的關聯時,效能會很差。
四、對含有關聯的PO(持久化物件)時,若default-cascade="all"或者 “save-update”,新增PO時,請注意對PO中的集合的賦值操作,因為有可能使得多執行一次update操作。
五、在一對多、多對一的關係中,使用延遲載入機制,會使不少的物件在使用時才會初始化,這樣可使得節省記憶體空間以及減少資料庫的負荷,而且若PO中的集合沒有被使用時,就可減少互資料庫的互動從而減少處理時間。
六、對於大資料量新增、修改、刪除操作或者是對大資料量的查詢,與資料庫的互動次數是決定處理時間的最重要因素,減少互動的次數是提升效率的最好途徑,所以在開發過程中,請將show_sql設定為true,深入瞭解Hibernate的處理過程,嘗試不同的方式,可以使得效率提升。
七、Hibernate是以JDBC為基礎,但是Hibernate是對JDBC的優化,其中使用Hibernate的緩衝機制會使效能提升,如使用二級快取以及查詢快取,若命中率較高明,效能會是到大幅提升。
八、Hibernate可以通過設定hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等屬性,對Hibernate進行優化。
九、不過值得注意的是,一些資料庫提供的主鍵生成機制在效率上未必最佳,大量併發insert資料時可能會引起表之間的互鎖。資料庫提供的主鍵生成機制,往往是通過在一個內部表中儲存當前主鍵狀態(如對於自增型主鍵而言,此內部表中就維護著當前的最大值和遞增量),之後每次插入資料會讀取這個最大值,然後加上遞增量作為新記錄的主鍵,之後再把這個新的最大值更新回內部表中,這樣,一次Insert操作可能導致資料庫內部多次表讀寫操作,同時伴隨的還有資料的加鎖解鎖操作,這對效能產生了較大影響。因此,對於併發Insert要求較高的系統,推薦採用uuid.hex 作為主鍵生成機制。
十、Dynamic Update 如果選定,則生成Update SQL 時不包含未發生變動的欄位屬性,這樣可以在一定程度上提升SQL執行效能.Dynamic Insert 如果選定,則生成Insert SQL 時不包含未發生變動的欄位屬性,這樣可以在一定程度上提升SQL執行效能
十一、在編寫程式碼的時候請,對將POJO的getter/setter方法設定為public,如果設定為private,Hibernate將無法對屬性的存取進行優化,只能轉而採用傳統的反射機制進行操作,這將導致大量的效能開銷(特別是在1.4之前的Sun JDK版本以及IBM JDK中,反射所帶來的系統開銷相當可觀)。
十二、在one-to-many 關係中,將many 一方設為主動方(inverse=false)將有助效能的改善。
十三、由於多對多關聯的效能不佳(由於引入了中間表,一次讀取操作需要反覆數次查詢),因此在設計中應該避免大量使用。
以上便是對Hibernate效能優化技巧總結,希望能對您有所幫助。
========================================================
批量刪除和修改
Hibernate是面向物件的,如果要對資料進行修改要先載入物件後修改,這樣的操作對於修改單條資料是沒什麼問題的。如果要更新批量資料,就可能佔用大量系統記憶體、需要執行多條select/update語句導致系統性能嚴重下降。所以在需要批量更新資料的時候,最好不要用上述方法。那麼有什麼更好方法麼?那就是使用HQL語句。
HQL語句可以像用SQL那樣進行批量操作資料。使用Hql就可以一次更新多條資料,而不用每次只更新一條資料了。
如:
更新User
Session session=factory.openSession();
Transaction tx=null;
try {
tx=session.beginTransaction();
//批量更新User
String hql="update User u set u.age=20 where u.age<=18";
//批量刪除,String hql=”delete User u where u.age>25”;
Query query=session.createQuery(hql);
int result=query.executeUpdate();
tx.commit();
} catch (HibernateException e) {
if(tx!=null) tx.rollback();
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
session.close();
}
選擇確當的查詢方法
Hibernatne 查詢分為兩類:一類是得到單個物件,get()和load方法;另一類是得到結果集,list()和iterator()方法。
get()方法和load()方法的區別在於對二級快取的使用上。load()方法會使用二級快取,而get()方法在一級快取沒有找到的情況下會直接查詢資料庫,不會去二級快取中查詢。在使用中,對使用了二級快取的物件進行查詢時最好使用load()方法,以充分利用二級快取來提高檢索的效率。
list()方法和iterator()方法之間的區別可以從以下幾個方面來進行比較。
1、 執行的查詢不同
list()方法在執行時,是直接執行查詢結果所需要的查詢語句,而iterator()方法則是先執行得到物件ID的查詢,然後再根據每個ID值去取得所要查詢的物件。因此,對於list()方式的查詢通常只會執行一個SQL語句,而對於iterator()方法的查詢則可能需要執行N+1條SQL語句(N為結果集中的記錄數)。
iterator()方法只是可能執行N+1條資料,具體執行SQL語句的數量取決於快取的情況以及對結果集的訪問情況。
2、 快取的使用
list()方法只能使用二級快取中的查詢快取,而無法使用二級快取對單個物件的快取(但是會把查詢出的物件放入二級快取中)。所以,除非重複執行相同的查詢操作,否則無法利用快取的機制來提高查詢的效率。
iterator()方法則可以充分利用二級快取,在根據ID檢索物件的時候會首先到快取中查詢,只有在找不到的情況下才會執行相應的查詢語句。所以,快取中物件的存在與否會影響到SQL語句的執行數量。
3、 對於結果集的處理方法不同
list()方法會一次獲得所有的結果集物件,而且它會依據查詢的結果初始化所有的結果集物件。這在結果集非常大的時候必然會佔據非常多的記憶體,甚至會造成記憶體溢位情況的發生。
iterator()方法在執行時不會一次初始化所有的物件,而是根據對結果集的訪問情況來初始化物件。因此在訪問中可以控制快取中物件的數量,以避免佔用過多快取,導致記憶體溢位情況的發生。使用iterator()方法的另外一個好處是,如果只需要結果集中的部分記錄,那麼沒有被用到的結果物件根本不會被初始化。所以,對結果集的訪問情況也是呼叫iterator()方法時執行資料庫SQL語句多少的一個因素。
所以,在使用Query物件執行資料查詢時應該從以上幾個方面去考慮使用何種方法來執行資料庫的查詢操作。
=========百度文庫資料的hibernate優化======================
優化總結?
???????????
要想優化Hibernate,我們必須知道應該從什麼地方進行優化,從什麼地方入手。Hibernate的優化方向:資料庫設計、HQL優化、快取、主配置、延遲載入、方法選用、集合選用、事物控制、批量操作
第一點:資料庫設計?
???????前邊部落格介紹過?資料庫設計,今天我們還從這開始,表的設計就是建樓的基礎,如何讓基礎簡潔而結實這是最重要的。
優化策略:?1.???建索引?
2.???減少表之間的關聯?
3.???簡化查詢欄位,沒用的欄位不要,已經對返回結果的控制,儘量返回少量資料?4.???適當的冗餘資料,不過分最求高正規化.
第二點:HQL優化
第三點:快取?
執行機制:?
介於應用程式和物理資料來源之間,其作用是為了降低應用程式對物理資料來源訪問的頻數,從而提高執行效能。?
快取被廣泛應用的用於優化資料庫。當一些資料被從資料庫中讀取出來的時候,我們可以把它們放到快取裡。這樣我們可以再次使用的時候直接從快取中取出來,這樣我們的效率就提高了很多。?
控制範圍:?
一級快取是session物件的生命週期通常對應的一個數據庫事務或者一個應用事務,它是事務範圍內的快取
二級快取是一個可插拔的快取外掛,它是由SessionFactory負責管理。由於SessionFactory物件的生命週期和應用程式的整個過程對應,所以二級快取是進城範圍或者叢集範圍內的快取。用於初始化很少更改的資料、不重要的資料,不會併發訪問的資料。
Hibernate快取的一些問題和建議:hibernate的快取?
第四點:捉取策略?
1.??捉取優化:Hibernate在關聯關係之間進行導航,充分利用Hibernate提供的技術?
2.??如何捉取?
立即捉取:當捉取宿主物件時,同時捉取其相關物件和關聯集以及屬性?
延遲載入:當捉宿主物件時,並不捉取其關聯物件,而是當對其物件進行呼叫時才載入。?
3.??捉取粒度:設定捉取個數?
第五點:批量資料處理(修改和刪除)?
在Hibernate2中,如果需要對任何資料進行修改和刪除操作都需要先執行查詢操作,在得到資料後才進行修改和刪除。?
1.??不適用Hibernate?API而是直接使用JDBC??API來做原生態SQL語句進行查詢,這種方法比較好,相對來說較快。
2.??運用儲存過程?
3.??一定量範圍內可以使用hibernate?API,但是特大資料量不行。
第六點:結果集的使用:?
?????
結果集的使用:list()和iterator()區別?查詢方式:?
list只能利用查詢快取(但在交易系統中查詢快取作用不大),無法利用二級快取中的單個實體,但是list查出的物件會寫入二級快取,但它一般只生成較少的sql語句,很多情況就是一條。?
iterator則利用二級快取,對於一條查詢語句,它會先從資料庫中找到所有符合條件的記錄的ID,在通過ID去快取找,對於快取中沒有的記錄,在構造語句從資料庫查出,第一次的執行會產生N+1條SQL語句。?產生結果:?用list可能會溢位?
通過Iterator,配合快取管理API,在海量資料查詢中可以很好的解決記憶體問題。?綜合考慮:?
一般List會填充二級快取,卻不能利用二級快取,而Iterator可以讀二級快取,然而無法命中的話,效率很低效。一般處理方法,就是第一次查詢使用list,隨後使用iterator查詢。