2011-02-09

The 5 most critical things to consider for proper collections usage in Hibernate

資料來源:Hibernate mapped collections performance problems

假設單向一對多的關聯:

<class name="Library">
 <id name="id">
  <generator class="sequence"/>
 </id>
 <set name="visits">
  <key column="library_id" not-null="true"/>
  <one-to-many class="Visit"/>
 </set>
</class>
<class name="Visit">
 <id name="id">
  <generator class="sequence"/>
 </id>
 <property name="personName"/>
</class>


在子物件的數量非常龐大時:

問題一、因為是用 Set,所以當增刪子物件時,Hibernate 為了確保 Set 的 unique,必須載入整個 collection,逐一檢查唯一性,出現效能瓶頸。


問題二、改用 List 取代 Set,當增刪子物件時,Hibernate 為了確保 List 的 order,仍舊必須載入整個 collection,效能瓶頸還是在。

問題三、改用 Bag 呢?可以完全避開 Set 的 unique 與 List 的 order,但是每次增刪物件時,主物件的 version number 必須加一,在一對多的關聯中效能影響不大,但是在多對多的關聯中,就無法估計受影響的物件了。

解法:完全不要用關聯!

  • 手動建立子物件,並將主物件 id 存於 子物件中。
  • 移除子物件時,直接移除子物件即可。
  • 移除主物件時,需手動同步移除所有關聯子物件。
  • 得自行負責 unique 或者 order。

如果使用單向多對一的關聯,仍舊有上述問題三的狀況出現。

那到底什麼時候可以使用 mapped collections?當 collection 很小時!

延伸討論:
  • inverse="true"
    • 建議在一對多的父子關聯中一定要使用。
    • 可以讓 child 握有關連的主控權。
    • 可以減少一次 foreign key 的 insert sql。
    • 可以避免 Set 的 unique 檢查。
    • 對 Component Collection 無作用。
  • 盡量不要呼叫 lazy 的 getChildred()。
  • 一定要實做 equals() 和 hashcode()
    • Hibernate 用到的物件都要實做 equals() 和 hashcode(),包括 Value Type object。
    • 如果沒有適當地 property 可供實做 equals() 和 hashcode(),使用 uuid 之類的 surrogate key。
    • 在沒有實做 equals() 和 hashcode() 的情況下,Hibernate 要新增一個物件,假設 db 裡已經有同一個物件存在,因為沒有實做 equals() 和 hashcode() ,所以 Hibernate 認為 db 無該物件存在,直接執行 insert,而且可能執行 delete 刪除 db 裡的舊物件。
    • Hibernate 知道的愈多,愈能有效率的運作。
  • 使用 version
    • Entity 一定要使用 version。
    • 使用 version 可以減少 Hibernate 往返 db 的次數。
  • Eager Fetch
    • 不建議使用 fetch=select,會造成 N+1 Select。
    • 建議使用 fetch=join,可以在同一個 sql 取得 parent 和 children。
    • 更建議使用:lazy by default, eager fetch on demand。
    • 參考:到底是誰用了fetch="select"!
  • Value Type object 使用 Surrogate Key
    • 因為 Hibernate 會以 parent 的 primary key 與 Value Type object 的所有 not null property 作為Value Type object 的 primary key,這會讓 pk 變得很奇怪,尤其是當 not null property 有 Date 型別時,因為 Date 包含到毫秒,所以可能讓應該相同的 Value Type object 因為毫秒的差異變成不同,也會影響到 db 的 index 效能。
    • 建議使用 Surrogate Key 做為 Value Type object 裡唯一 not null property,如此可以改善 db 效能。
    • 但是,這個 Surrogate Key 主要用來改善 db 效能,盡量不要用在 equals() 和 hashcode() 裡。

沒有留言:

張貼留言