今天在處理效能問題時遇到一個 Hibernate 設定造成的效能問題,在一對多的關聯裡使用了以下的設定,導致 N+1 Select 的問題。
<set name="childred" inverse="true" lazy="false" fetch="select" cascade="all-delete-orphan">
在資料量不大時,這問題不明顯,但是當一次要 select 幾千筆資料時,每一筆資料都會另外執行一個 select 來取出 children 資料,這時 N+1 Select 就影響整個效能。
可以在下 HQL 時使用 eager fetch 來解決這個問題。
select p from Parent p join fetch children c where ....;
這樣取到的 parent 物件的 children 就已經是 initialized 的了。
猜測會使用 fetch="select" 的原因應該是資料筆數的問題,當使用 eager fetch 時,select 到的資料不是 parent 的筆數,而是 child 的筆數,所以在某個特別的情況,為了解決 select 筆數的問題用了 fetch="select" 這個毒藥。
一對多的關聯建議用法是 - lazy by default, eager fetch on demand,就是預設用 lazy,當有需要的時候,再用 eager fetch 去取得其他資料。
但是做得到 eager fetch by default, lazy on demand 嗎?
eager fetch :
回覆刪除父(50欄位):1筆,子(20欄位):1000筆
=>查詢結果是
[父(50欄位)+ 子(20欄位)] * [1*1000]筆
父(50欄位):50筆,子(20欄位):100筆
=>查詢結果是
[父(50欄位)+ 子(20欄位)] * [50*100]筆
不清楚樓上的意思,但是
回覆刪除父(50欄位):50筆,子(20欄位):100筆
=>查詢結果是
[父(50欄位)+ 子(20欄位)] * [50*100]筆
=>如果只是一般的一對多,那應該是
[父(50欄位)+ 子(20欄位)] * [100]筆
不是嗎?