鑑於反覆討論hibernate適用性問題,希望有定論

Robbin:

其實圍繞Hibernate的話題,我都已經說過不下30遍,以致於最近兩年以來,我對所有Hibernate的問題都不願意再回應。另外最近一年多來,使用Rails的ActiveRecord,讓我對ORM的認識又加深了很多,其實對於那麼多爭議的問題,最好的解決辦法就是自己去實踐。對於自己沒有去實踐過的東西,爭是爭不出來什麼的。
-------------------------------------

引用

1、以數據庫爲中心建模 VS 以領域模型爲中心建模:
老開發人員大多傾向於前者,因爲比較符合過去的開發習慣,另外他們強調數據庫的生命週期大於App
向我這樣的只有幾年工作經驗的往往會傾向於後者,因爲這能更充分發揮ORM的威力,更符合OO,免去很多維護DB的繁瑣工作。
-------------------------------------
數據庫設計三大範式如雷貫耳,但作爲非科班出身的我直到兩個月前竟然都不知道三大範式究竟是什麼。那麼三大範式是什麼?
-------------------------------------
引用
第一範式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。
第二範式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況),也即所有非關鍵字段都完全依賴於任意一組候選關鍵字。
第三範式(3NF):在第二範式的基礎上,數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三範式。所謂傳遞函數依賴,指的是如果存在"A → B → C"的決定關係,則C傳遞函數依賴於A。

-------------------------------------
兩個月前當我購買了一本《MySQL權威指南》,翻到三大範式的定義的時候,我內心巨震,三大範式簡單總結一句就是消除冗餘,單純依賴關係。不允許數據庫表出現冗餘字段,不允許表之間多重依賴,因此符合三大範式設計的數據庫模型其實和你按照面向對象思想去建模得到的數據庫模型是一樣的。
所以不論你是從數據庫爲中心建模,還是你以領域模型爲中心建模,你應該最終得到一個一致的數據庫模型之所以導致數據庫建模和OO建模的不一致,是因此傳統的數據庫建模從來都是違背三大範式的。而我們在過去經常說的一句話就是:爲了數據庫查詢性能,我們需要多加一些冗餘字段,不一定非要遵循三大範式......
所以不要再說什麼數據庫設計和麪向對象設計導致的數據模型衝突的話,不是他們衝突,是你違背了三大範式,自己製造出來的衝突。
-------------------------------------
引用

2、Hibernate VS iBatis/JDBC:
擔心失去對SQL待控制權,導致不能做優化,DBA反對
Hibernate是在JDBC之上的又一層框架,因此想當然的認爲其性能不如iBatis/JDBC(我認爲這個結論不成立,因爲引入一個ORM層給了我們更多機會去優化性能,比如一二級緩存、lazyload、查詢緩存,並且方式更優雅)。參考爲什麼ORM性能比iBATIS好?
擔心OpenSessionInView模式有性能問題(http://www.javaeye.com/topic/17501)
Hibernate無法應付複雜查詢(我認爲這不是問題,HQL和criteria查詢能力很強,再不濟還可以用SQL啊)

-------------------------------------
JavaEye網站的數據庫設計是面向對象爲中心的設計,但是拿三大範式來衡量,大部分設計都是吻合的,而我們的數據庫緩存命中率在90%左右。緩存服務器的流量是數據庫服務器流量的2.5倍之多。事實上我們有很多地方的查詢儘量避免join,寧可讓他n+1,這樣速度反而更快,緩存命中率更高。
例如我們現在把帖子的內容字段拆分出來,單獨放在一個post_texts表裏面。這樣posts表實際上只有35MB,而post_texts表有1GB。每次顯示一個post,都會用主鍵去load post_text,命中緩衝。不需要查數據庫,不需要去碰那個1GB的大表。
要充分發揮ORM的緩存優勢,就必須把表設計的儘量細顆粒度,消除冗餘和多重依賴,最終可能是相當多的小表,表之間通過主外鍵關聯,但是關聯關係都是單一的。那麼這種追求面向對象的數據庫模型是非常符合三大範式的。
-------------------------------------
引用

3、對Hibernate等ORM框架能否勝任大型項目的懷疑:
其實項目大小不是技術選型的主要考慮,關鍵看項目類型,OLTP還是OLAP、廣而淺型的還是窄而深型的、數據量大小等等,這些因素更能影響結果

-------------------------------------
我只想舉一個例子,Google在使用Hibernate,並且Google開發了一個叫做Hibernate Shards的分佈式工具,將Hibernate運用在大規模分佈式數據庫環境當中,Hibernate Shards現在也是開源的,在Hibnerate網站上面就可以找到文檔
-------------------------------------
引用

4、Hibernate學習成本高
不可否認,相對於spring、struts,Hibernate是一個學習曲線陡峭的框架,但是我覺得綜合考慮開發效率和長期收益,還是值得學習和採用的

-------------------------------------
有時候參考着看看Rails的ActiveRecord,你才能發現ORM的樂趣。橫向對比一下,你的思路會開闊很多,認識也會客觀一些。

推薦有空閱讀我寫的相關文章:
漫談應用緩存的命中率問題
爲什麼ORM性能比iBATIS好?
緩存簡述

 

有一點必須承認,Hibernate雖然是一個功能非常強大的ORM,但是他在使用上面的陷阱也非常多,稍一疏忽就可能導致性能問題,所以真正把Hibernate用得好的項目非常罕見(當然也有用得好性能非常棒的)。即使是Rails的ActiveRecord,如果不注意,也很容易導致性能上面的問題,當然ActiveRecord功能簡單的多,所以問題不像Hibernate那樣突出。另外ActiveRecord的log輸出的SQL非常易讀,所以比較容易及時發現問題,不像Hibernate的log輸出的SQL,根本就不是給人看的,出了問題很不容易發現。

我認識的很多相當資深的開發人員其實也不能夠正確的認識ORM的作用,對Hibernate持相當否定的態度。我覺得這種狀況一點都不奇怪,其實我自己也是在用了Rails的ActiveRecord,並且在JavaEye網站摸索了那麼長時間,纔對ORM能夠有一個全面的認識的。

總之,follow your heart! 用你自己認爲是正確的方式去實踐和總結、不要理會別人的觀點。

 

說到訪問數據庫的性能問題,其實用不用ORM,或者用不用iBATIS,這些都是皮毛,甚至用不用緩存,怎麼用緩存,也沒有觸及到本質問題,真正的本質在於“數據庫的瓶頸在於訪問數據庫文件的磁盤IO,儘量減少數據庫服務器的磁盤IO才能從本質上提高數據庫吞吐量”圍繞這個本質,需要你從操作系統層面,數據庫層面,應用程序的架構設計,緩存方式和應用程序框架方面進行通盤的考慮,至於說用不用Hibernate只是很小的一個戰術層面而已
=======================

lgx522:

以前對三大範式印象太深,以致於一直是採用DB建模,最後設計出來的結果基本上是符合OO的,還真是殊途同歸,所以ORM用得很爽。Hibernate有些複雜了,用起來還是要琢磨太多東西。最近用ActiveRecord,這才真正有飛起來的感覺。
另外說一點,大家不要太迷信那些所謂DBA的鬼話,好像離了冗餘就沒辦法過日子。事實上經過良好設計符合第三範式與及OO的數據庫應用,性能是相當高的,並且可以利用中間層緩存解決大部分性能問題。那種想以DB爲中心搞定一切的錯誤思想是過時了。
在這種藉口下,太多既不符合範式,也不符合OO的RAD垃圾系統橫行市場,已經令人無法忍受了。

=======================

引用
就我自己使用hb的經驗來看,其實我也認同很多性能問題其實都是不洽當的使用hb或者錯誤的數據庫設計、錯誤的編程模式造成的。比如:在我自己負責的多個項目中就發現很多程序員喜歡在循環中去做一些查庫操作;或者條件查詢時根本就不使用分頁查詢;或者在不知道什麼是lazyload以及get方法會引起查庫操作等等的情況下。就在循環當中去調用get方法。
說實話:我在建行的很多j2ee項目中【包括建總行級的項目】都發現了以上的編程模式造成的性能問題,並且自己也親自去解決優化過這些問題。
但是很多銀行的所謂“高人”們在一遇到這種問題時往往會做出類似“看看,java性能確實不好”、“hb有性能問題”這樣的結論。
其實我只是覺得非常的好笑。採用錯誤的編程模式,不管你用hb還是jdbc還是ibatis都會有性能問題的!!
-------------------------------------
是的,任何編程方法都會有自己的陷阱,譏刺Java性能不好,那C++程序員寫的代碼弄不好就把操作系統搞crash的又怎麼說?

RoR的ActiveRecord照樣有陷阱,他的1:n關係當中的_count魔法字段當不使用counter_cache聲明的時候,在update操作當中會帶來致命的性能問題。比方說topics表當中有一個posts_count字段,而topic和post有1:n的關係聲明,那當程序員手工更新posts_count字段的時候,ActiveRecord就發送一條: select * from topics的SQL,把整個topics表全部抓出來,可怕吧!

程序員編程自己不注意代碼質量,就是不用ORM,光用JDBC,難道就寫不出來爛代碼?難道Hibernate沒有問世之前,Java程序員從來就不會碰到訪問數據庫的性能瓶頸嗎?我們2000年的時候寫的JDBC爛代碼不使用榜定變量,導致Oracle數據庫每週都要crash一次。
-------------------------------------
firmgoal 寫道
我希望達到的目標,數據庫查詢返回應該是毫秒級的(<1s)。在實踐中發現大表關聯基本上沒有毫秒級的,因此,我還是強調我的觀點,可以通過冗餘來避免關聯,減少IO。

-------------------------------------
我手裏沒有Oracle數據庫,所以沒有辦法做相應的測試,但是我覺得你的查詢結果不可思議,查詢毫無道理的慢,除非你的student表和resume表的字段太多,或者包含了大字段。像我在MySQL上面做的查詢,但凡有效利用索引的關聯查詢,都不會超過1秒鐘。

我建議你再測試一下單表查詢,單獨select student表和select resume表,加上同樣的where條件和分頁語句,看看執行時間。如果你的student和resume表關聯查詢這麼慢的話,單獨select resume表也不可能快的起來。

另外你的student表多少個字段?有沒有blog/clob/long型的大字段,現在有多少條記錄,表存儲空間多大? resume表有多少個字段?有沒有blob/clob/long型大字段,現在有多少條記錄,表存儲空間多大?這個信息很重要!

實際上關聯查詢並不一定慢,只要關聯外鍵有索引,where條件有效索引可以約束掃描的表記錄,關聯查詢並不見得會比單表查詢慢,關鍵還是看查詢語句實際造成了多少磁盤IO。所以查詢性能低下的根源不在於關聯查詢,而在於表掃描造成的IO。

 

我仔細看了一遍你貼出來的執行計劃,終於發現你關聯查詢慢的根源了!請看:
s.guid=r.stu_guid
你的關聯查詢關聯外鍵上面根本就沒有建立索引!這種關聯查詢會造成你的student表和resume表全表掃描,難怪會這麼慢!
給student表的guid字段建立索引,然後給resume表的stu_guid字段也建立索引,再查詢一遍,我保管你1秒鐘查詢完畢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章