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