Hibernate的性能(轉(zhuǎn)貼)
Hibernate的性能(轉(zhuǎn)貼)
xiecc:
我們的項(xiàng)目從去年12月份啟動(dòng),采用了Struts+Hibernate的架構(gòu),一開始使用Hibernate的時(shí)候速度極快,對(duì)象操作異常方便,大家都說爽歪歪。
可惜好景不長(zhǎng),隨著我們對(duì)象關(guān)系的不斷復(fù)雜,數(shù)據(jù)量的不斷增加,Hibernate的性能急劇下降。具體表現(xiàn)為:我們?cè)谠O(shè)計(jì)對(duì)象時(shí)采用了很多的one-to-many和many-to-one的關(guān)系,在取某個(gè)對(duì)象的幾個(gè)簡(jiǎn)單的屬性時(shí),它會(huì)把所有關(guān)聯(lián)的子對(duì)象都取出來,經(jīng)常出在取一個(gè)簡(jiǎn)單屬性的時(shí)候,調(diào)試窗口的SQL語句一屏一屏地往下閃。到最后我的一個(gè)test跑完需要12分鐘。
在忍無可忍之下,我們開始性能優(yōu)化方案,以下我們優(yōu)化所做的一些事情:
1、將所以one-to-many的關(guān)系里將lazy設(shè)成true
2、修改hibernate.properties,增加了以下兩句:
hibernate.jdbc.fetch_size=50
hibernate.jdbc.batch_size=100
3、調(diào)整WebLogic的pool
4、利用Hibernate提供的CGLIB Proxy機(jī)制,使many-to-one關(guān)系的子對(duì)象也可以lazy initialization
(但是我發(fā)現(xiàn)調(diào)試窗口里仍會(huì)有取子對(duì)象的SQL語句,但速度確實(shí)快了)。
5、利用Hibernate提供的Cache機(jī)制,對(duì)關(guān)鍵對(duì)象使用Cache
結(jié)果優(yōu)化以后,我的test可以從原來的12分鐘變成50秒鐘跑完。
原以為萬事大吉了,但當(dāng)我們面對(duì)客戶的時(shí)候,才發(fā)現(xiàn)我們系統(tǒng)的性能還遠(yuǎn)遠(yuǎn)不夠。
我們現(xiàn)在系統(tǒng)試運(yùn)行約兩個(gè)月,經(jīng)常在數(shù)據(jù)保存或者查詢時(shí)等上一分鐘甚至兩分鐘。
由于客戶原來的系統(tǒng)用asp+SQL Server寫的,速度很快。二者一對(duì)比,我們就被客戶罵得慘不忍睹。
優(yōu)化真是一件很煩人的事,在不改動(dòng)系統(tǒng)框架的情況下,不知還有哪些提高系統(tǒng)性能的方法?
freecode:
同感,雖然我不用,不懂hibernate.
前段時(shí)間,我們做了個(gè)項(xiàng)目,對(duì)一些取數(shù)的過程,采用了javascript腳本,再通過bsf編譯,運(yùn)行時(shí),時(shí)間巨長(zhǎng),人家說以前用foxpro做的,快多了,弄得我們很沒面子。
dhj1:
我也用 Struts+Hibernate 做大型項(xiàng)目,在并發(fā)很高時(shí),每天4500人次訪問量的情況下,性能也相當(dāng)不錯(cuò).
做的時(shí)間有幾點(diǎn)考慮:
1.大東東.如果很多很多的one-to-many和many-to-one的關(guān)系. 必定會(huì)影響性能,我剛學(xué)習(xí)Hibernate 時(shí)就有這種直覺,所以我們沒有用one-to-many和many-to-one的關(guān)系.而是象SQL一樣的去操作表的關(guān)系標(biāo)識(shí)符.
2.如果超大的系統(tǒng),最終必須生成HTML的文件.就是有數(shù)據(jù)庫中有數(shù)據(jù)更新時(shí),自動(dòng)生成一個(gè)HTML文件.大多數(shù)用戶是在只讀狀態(tài).在只讀狀態(tài)下就只去顯示HTML文件,節(jié)省很多資源.
3.更用CHACHE表技術(shù),把訪問量高的記錄自動(dòng)提到CACHE表中.
xiecc:
謝謝dhj1給我提的建議。
在hibernate網(wǎng)站上看到的好多資源幾乎都說hibernate的性能不錯(cuò),而且很多人開發(fā)的系統(tǒng)性能也不錯(cuò),包括dhj1的,看來hibernate無罪,是我們?cè)O(shè)計(jì)得太濫了。
不過還是有點(diǎn)疑問。
1、dhj1提到的第一點(diǎn)很有道理。我們確實(shí)在有些關(guān)鍵的地方用了標(biāo)識(shí)符來關(guān)聯(lián)。但是我們這個(gè)系統(tǒng)的關(guān)聯(lián)實(shí)現(xiàn)太多了,如果所以有東西都用標(biāo)識(shí)符作關(guān)聯(lián)的話,那我們的實(shí)體層設(shè)計(jì)就退化成為面向關(guān)系的ER圖設(shè)計(jì),那我們要Hibernate何用?我用感覺用Hibernate時(shí)最大的便利不是在寫代碼的時(shí)候用對(duì)象的操作代替SQL語句,而是在建模的時(shí)候可以用面向?qū)ο蟮乃季S把很復(fù)雜的邏輯用UML圖表示出來,然后直接轉(zhuǎn)化成實(shí)體。所以我們?cè)谛阅苡绊懱蟮牡胤讲捎昧嗣嫦驅(qū)ο蠛完P(guān)系相結(jié)合的方式,但在更多的地方仍然只能采用對(duì)象關(guān)聯(lián)的方式。
2、生成靜態(tài)HTML,對(duì)特定的系統(tǒng)確實(shí)有用,但我們系統(tǒng)中的數(shù)據(jù)幾乎都是動(dòng)態(tài)的,所以實(shí)現(xiàn)起來有困難。而且我也不太清楚這樣做的難度有多高,具體怎么實(shí)現(xiàn),請(qǐng)dhj1指點(diǎn)迷津。
3、Cache我們已經(jīng)采用了。但proxy的用法我至今仍然有點(diǎn)迷糊。
在Hibernate的文檔里似乎只要在定義文件里加這么一句就可以了:
<class name="eg.Order" proxy="eg.Order">
但實(shí)際使用時(shí)我們發(fā)現(xiàn)這樣做之后,Hibernate取數(shù)據(jù)時(shí)的SQL語句似乎一句都沒有少。
我們現(xiàn)在的想法是自已來實(shí)現(xiàn)Proxy類,它的接口與實(shí)體完全一樣,在Proxy里SQL語句來實(shí)現(xiàn)取數(shù)據(jù)操作。不知是否可行?
sanwa:
我在設(shè)計(jì)系統(tǒng)時(shí),對(duì)每個(gè)對(duì)象圖都會(huì)采用兩套映射模型,
一套用于映射實(shí)際的UML,當(dāng)然包括引用,這套對(duì)象圖主要用于根據(jù)關(guān)聯(lián)對(duì)象的屬性來查詢對(duì)象時(shí)使用,很少用于更新數(shù)據(jù),除非是聚集類.
另一套映射,把對(duì)象引用映射為L(zhǎng)ong型的屬性,傳遞到業(yè)務(wù)層或表示層,需要相關(guān)的對(duì)象引用時(shí),再用這個(gè)引用來load對(duì)象.而且,對(duì)于需要load的對(duì)象可以使用代理或直接多映射一個(gè)簡(jiǎn)單類來處理.
另外,對(duì)于大數(shù)據(jù)量的查詢或表的關(guān)聯(lián)層次較深(超過三層),建議采用jdbc直接處理.
方世玉:
我們現(xiàn)在也正在用hibernate進(jìn)行項(xiàng)目開發(fā)
我認(rèn)為不是所有相關(guān)的表都要做one Many,many one映射的
比如說一個(gè)用戶和他的定購業(yè)務(wù),我們做了雙向關(guān)聯(lián)。
但是這個(gè)用戶的話單就不能和用戶做任何關(guān)聯(lián)了。話單表每天都有數(shù)十萬條,就是做manyone關(guān)聯(lián)也非常嚇人,寧可到時(shí)候用hql來查詢
xiecc:
呵呵,請(qǐng)教sanwa,這兩套映射模型如何在一個(gè)應(yīng)用中同時(shí)使用?
dhj1:
我用hibernate,是可以減少開發(fā)工作量. 特別在開發(fā)中或維護(hù)過程中對(duì)表結(jié)構(gòu)的改動(dòng),用HIBERNATE是很方便的.
做了DAO后,對(duì)表的父子關(guān)系等的處理,通過ID標(biāo)識(shí),也只是兩三句程序語句.操作也很方便,而且更靈活.
生成靜態(tài)HTML,以后我做過這樣的系統(tǒng),并在XXX省信息港網(wǎng)站上大量使用,性能當(dāng)然不錯(cuò),同時(shí)1400人在線(真正的在線,不是那種用網(wǎng)頁搞個(gè)幾分鐘刷新一次延時(shí)的那種在線),性能也不錯(cuò),開發(fā)當(dāng)然會(huì)有一些難度.
我說的CACHE不是說用HIBERNATE的CACHE.而是自已開發(fā)的,把訪問量高的信息自動(dòng)放到一個(gè)表中去,這個(gè)表保證只有100訪問量最高的條記錄.多于100條記錄的就出去了.
sanwa:
舉個(gè)例子,比如用戶的權(quán)限,在我們的系統(tǒng)中涉及用戶(User)、權(quán)限(Acl)、組件(Component)、組件類(componentDomain)等幾個(gè)對(duì)象的關(guān)聯(lián)。
第一套映射圖,反映他們的實(shí)際關(guān)系,即對(duì)應(yīng)UML模型。User和Acl的關(guān)聯(lián)映射為idbag,不要直接映射為set,因?yàn)樵谖以O(shè)計(jì)的關(guān)聯(lián)表中存在代理主鍵,代理主鍵在第二套映射圖中實(shí)際為用戶權(quán)限的id.
第二套映射圖,只映射用戶(UserSO)和用戶擁有的權(quán)限(UserAclSO),是one to many的關(guān)系。
后綴SO表示相關(guān)類的簡(jiǎn)單對(duì)象映射類。
這樣,當(dāng)客戶端需要獲取某個(gè)用戶的所有權(quán)限時(shí),直接用第二套映射圖。返回的集合中就只包括Acl的id。如果要獲取用戶對(duì)某個(gè)組件域的權(quán)限,則用第一套映射圖,用強(qiáng)大的HQL查詢,再轉(zhuǎn)換為第二套映射圖返回到客戶端。
當(dāng)更新用戶的權(quán)限時(shí),也用第二套映射圖,直接操作UserSO和UserAclSO,傳回更新。
使用類似的設(shè)計(jì)策略時(shí),對(duì)many to many的關(guān)聯(lián)表,都采用代理主鍵,而不是聯(lián)合主鍵,這樣,兩套映射圖都較容易存取數(shù)據(jù)。只是,多數(shù)情況下用第二套映射圖。
當(dāng)然,我的程序架構(gòu)是Swing + Session Bean + DAO + Hibernate + DB,Swing和Session Bean的通信可以用HTTP或RMI,在我的架構(gòu)中,lazy loading發(fā)揮不了多大的作用,才采取這種策略。如果在lazy loading可以發(fā)揮作用的地方,對(duì)大多數(shù)對(duì)象圖,是沒有必要采取這種策略的。
另外,在我的架構(gòu)中,swing層有一個(gè)組件是可以根據(jù)一個(gè)ID來加載這個(gè)類的屬性,直接用jdbc實(shí)現(xiàn)的,獨(dú)立于Hibernate
xiaoyu:
我也說上一句吧。
雖然HB是好,方便,但有關(guān)數(shù)據(jù)庫設(shè)計(jì)的一些性能原則還是要考慮的。
畢竟它只是Mapping而已。
所以也要設(shè)置索引等東西。
jxb8901:
上面的proxy和cache沒有任何關(guān)系,只是用于lazy loading,請(qǐng)看hibernate中文文檔第5章:
java代碼:?
proxy (可選): 指定一個(gè)接口,在延遲裝載時(shí)作為代理使用。你可以在這里使用該類自己的名字。
coolwyc:
為了在性能上得到平行,對(duì)many-to-one不直接使用關(guān)連,例如:user&ACL,取user時(shí),不取ACL,要取ACL就先取user,再取ACL,畢竟應(yīng)用取user的頻率比取ACL高,沒必要在取user是硬要把ACL一起取出來.
還有其他many-to-one都和這個(gè)很類似,如果many很少的話就沒問題,例如:人&寵物,通常一個(gè)人只有少于等于一個(gè)寵物,這種情況取人的時(shí)候把人跟寵物一起取出對(duì)系統(tǒng)影響不大
willmac:
hibernate本身的性能非常好,關(guān)鍵在設(shè)計(jì)本身
和你的數(shù)據(jù)庫本身,以及你的訪問量和數(shù)據(jù)庫的性質(zhì)
針對(duì)不同的環(huán)境一定要有不同的設(shè)計(jì)的,一套設(shè)計(jì)肯定不能適用于全部的環(huán)境
我舉個(gè)典型例子來說吧
你的hibernate設(shè)計(jì)可以采用單session的方式,也可以采用多session的方式,應(yīng)用環(huán)境不同,結(jié)果也大大的不同。當(dāng)用戶人群少,數(shù)據(jù)庫記錄兩低的時(shí)候,多session的設(shè)計(jì)是非常有優(yōu)勢(shì)的,這就是為什么那么多人
反對(duì)使用threadlocal管理session的主要理由把,的卻
把兩種設(shè)計(jì)都放在這里,你會(huì)發(fā)現(xiàn)多session的性效要比
threadlocal的強(qiáng)太多了,并且編程也異常的容易,這種例子,我不再舉了,你在這個(gè)論壇上都可以下的到。可是
當(dāng)人群變多,數(shù)據(jù)庫記錄海量增長(zhǎng)的時(shí)候,我們發(fā)現(xiàn)問題來了,我們做的應(yīng)用無限制的吃去內(nèi)存,應(yīng)用的響應(yīng)開始變慢,這是為什么呢?不知道大家是否理解究竟什么是session,其實(shí)從根本上說就是一張hashmap,這樣大家就好理解了,當(dāng)不同的用戶,同時(shí)訪問應(yīng)用的時(shí)候,不同的人建立不同的連表,然后釋放,而當(dāng)并發(fā)人群急速增長(zhǎng)的時(shí)候,于是問題就來了。那怎么辦,threadlocal沒有其他的辦法,你要解決一個(gè)同步的問題,我們只有一個(gè)session,你不能夠同時(shí)往里讀取數(shù)據(jù),然后又寫入數(shù)據(jù)的,并發(fā)用戶這么多,你只可以讓他們一個(gè)一個(gè)得來!!!
哇,這樣效率不是太低了,的確,我一開始就說了,小型系統(tǒng),使用threadlocal絕對(duì)是低效的做法的,可是當(dāng)規(guī)模上來了之后,我們?cè)賮砜矗瑑?nèi)存不再無限制的開銷,cpu德負(fù)荷也降下來了,唯一一點(diǎn)發(fā)生變化的是,用戶的客戶端可能多等了0.001秒鐘,一個(gè)在服務(wù)段隊(duì)列的時(shí)間,這就是設(shè)計(jì)。
講了一個(gè)設(shè)計(jì)的例子,我們來看多對(duì)一,一對(duì)多,會(huì)讓我們應(yīng)用的效率降低么?
請(qǐng)注意hibernate只是幫助我們完成了mapping 的工作
原來的一對(duì)多也好或者多對(duì)一也好,本來就存在的,之所以讓你覺得效率低,是因?yàn)樵诘玫礁赣H后,還要去把每一個(gè)兒子給讀出來,可是注意,你只是多執(zhí)行了一條sql而已,為什么會(huì)慢的呢,我想問題還是在設(shè)計(jì)之上
?
posted on 2007-03-23 09:31 mlw2000 閱讀(260) 評(píng)論(0) 編輯 收藏 所屬分類: java 、databases