两种极端情况的案例:N+1次查询和笛卡尔积
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
前一篇文章兩種極端:頻繁的查詢和巨大的結(jié)果集講到了Hibernate加載數(shù)據(jù)時可能會出現(xiàn)的兩種極端情況:頻繁的查詢和一次查出巨大的結(jié)果集。其中:N+1次查詢是前一種情況的一個典型案例,笛卡爾積則是后一種情況的典型案例。下面分別簡單地再總結(jié)一下這兩種極端案例出現(xiàn)的原因以及調(diào)優(yōu)方法。
?
一.N+1次查詢
??? 如果一個集合是lazy loading的,那么在第一次訪問到這個集合時,hibernate會生成一個select被這個集合加載出來。這是N+1中的1。緊接著,在迭代這個集合的過程中,如果要訪問集合元素所依賴的其他關(guān)聯(lián)對象時,若它的關(guān)聯(lián)對象也是lazy loading的,那么hibernate會生成一個select從數(shù)據(jù)庫中加載出這個關(guān)聯(lián)對象。這樣,N次循環(huán)就會生成n個select,這就是N+1中的N.
??? 解決N+1次查詢需要在制定動態(tài)抓取策略時eager fetch出集合和集合元素的依賴對象,這樣只會生成一條SQL。具體做法是在HOL中,使用fetch關(guān)鍵字來抓取所需要的對象。在Criteria中使用setFetchMode方法設置目標抓取對象。具體示例請參考JPwH-13.5.2
?
?
二.笛卡爾積
??? N+1次查詢的反面就是笛卡爾積。一般來說,通過定制動態(tài)抓取策略,不會加載出我們不需要的數(shù)據(jù),但是有一種情況下,即使我們只加載必需對象也會造成大量數(shù)據(jù)被select出來,這就是抓取“平行”集合導致的笛卡爾積。
??? 例如:一個Forum有一個Moderator集合,大小為3和一個Thread集合,大小為100。如果在加載Forum時,單獨抓取Moderator集合,結(jié)果集是3,單獨抓取Thread集合,結(jié)果集是100,同時抓取這兩個集合的結(jié)果集是兩個集合的加乘(也就是笛卡爾積)為:3*100.如果還有第三個集合,大小是50的話,那結(jié)果集就會變成3*100*50.?因此我們可以看到:抓取“平行”集合會產(chǎn)生笛卡爾積,如果集合很多或集合中的元素很多,會使結(jié)果集急劇的膨脹。一個巨大的結(jié)果集所帶來的性能損失是什么呢?想想數(shù)據(jù)庫服務器處理這個條數(shù)據(jù)所花的時間,占用的服務器的內(nèi)存,通過網(wǎng)絡傳輸這些數(shù)據(jù),以即到了應用服務器占用的內(nèi)存和hibernate封裝這些數(shù)據(jù)所花費的時間,和這些開銷相比,分成兩三個SQL(會成數(shù)量級的減小結(jié)果集)來獲取數(shù)據(jù)要快得多得多。
??? HIbernate并不禁止我們產(chǎn)生笛卡爾積,也就是說,你可以在一個抓取計劃中抓取多個“平行”集合。但是有一個例外,就是bag集合。Hibernate不允許同時抓取兩個以上的bag集合。這是因為:The resultset of a product can’t be converted into bag collections, because Hibernate can’t know which rows contain duplicates that are valid (bags allow duplicates) and which aren’t.
??? 對于“平行”集合的問題,在抓取時要靈活應對,如果預計到結(jié)果集會很大,那么就不要使用join fetch,而要使用subselect fetch!subselect fetch是平行集合的推薦優(yōu)化方案。
??? 關(guān)于N+1次查詢和笛卡爾積,請參考JPwH_13.2.5節(jié)。
?
?
?
?
???
?
?
?
轉(zhuǎn)載于:https://my.oschina.net/pangzhuzhu/blog/326989
總結(jié)
以上是生活随笔為你收集整理的两种极端情况的案例:N+1次查询和笛卡尔积的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顺丰物流单号查询效果(可输入多个文本框)
- 下一篇: jquery解决和其他库冲突的问题