2011-02-08

到底是誰用了fetch="select"!

今天在處理效能問題時遇到一個 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 嗎?

2 則留言:

  1. eager fetch :
    父(50欄位):1筆,子(20欄位):1000筆
    =>查詢結果是
    [父(50欄位)+ 子(20欄位)] * [1*1000]筆


    父(50欄位):50筆,子(20欄位):100筆
    =>查詢結果是
    [父(50欄位)+ 子(20欄位)] * [50*100]筆

    回覆刪除
  2. 不清楚樓上的意思,但是
    父(50欄位):50筆,子(20欄位):100筆
    =>查詢結果是
    [父(50欄位)+ 子(20欄位)] * [50*100]筆
    =>如果只是一般的一對多,那應該是
    [父(50欄位)+ 子(20欄位)] * [100]筆
    不是嗎?

    回覆刪除