2010-06-15 89 views
2

我相信我可以提高休眠以下findByName查詢的性能:什麼是最快速的findByName查詢與休眠?

public List<User> findByName(String name) { 
    session.createCriteria(User.class).add(Restrictions.eq("name", name)).list(); 
} 

瓶頸是findByName方法,我不能使用id來代替。

在我的情況下,我知道該名稱是唯一的,但將名稱註釋添加到name屬性並沒有提高性能。我做了以下內容:

class User { 
    @Index(name = "nameIdx") 
    private String name;  
} 

在哪種方式,我應該改進它,甚至更重要的是:在這方面,我應該首先改善了嗎?我將需要這個類的所有集合(無論是否是)和全部的全部對象。

或者我可以改進它,如果我想要幾個用戶對象(並知道幾個名字)?

UPDATE1:

的@index註釋沒有提高性能,因爲數據庫已經有了一個指標,因爲我的唯一約束註釋:

@UniqueConstraint(columnNames = {"name"}) 

UPDATE2:

  1. 請仔細閱讀答案!

  2. 在SQL日誌的幫助下,我發現真正的問題是很多更新和插入語句被引發,儘管我沒有提交或刷新事務。 背後的原因是,我做了(在一個循環中):

    User u = findByName(name); 
    if(u == null) 
        attach(u = new User(name)); 
    

    等Hibernate需要每findByName查詢之前刷新新創建的用戶數據庫。我用我自己的緩存解決方法(LinkedHashMap)解決了這個問題。

  3. 我通過延Schauder不尖提出的另一項改進:

    Read this answer得到一個:

    public Collection<User> findByNames(Collection<String> names) { 
        return session.createCriteria(User.class). 
          add(Restrictions.in("name", names)).list(); 
    } 
    
  4. 進一步的改進可以指定一些用戶採集的時候不會偷懶進行更好的選擇。

  5. 最後也是最重要的一個對我來說是:用一個列表代替我的SortedSet項目,做的getItems方法如下:

    Set set = new LinkedHashSet(items); 
    items.clear(); 
    items.addAll(set); 
    Collections.sort(items, itemComparator); 
    return Collections.unmodifiableCollection(items); 
    

    與,Hibernate可以在項目收集工作(即添加)而不從數據庫加載整個集合。

@Pascal Thivent和@Jens Schauder不:一堆感謝的!對不起,我只能接受一個答案: -/

有用的日誌記錄設置:

log4j.logger.org.hibernate.tool.hbm2ddl=INFO, StdoutApp 
log4j.logger.org.hibernate.SQL=INFO, StdoutApp 
# additionally provide the information which parameters will be bound: 
log4j.logger.org.hibernate.type=TRACE 

+0

*(...)因爲數據庫已經有一個索引,因爲我唯一的約束註釋*:這很可能。你真的需要檢查查詢計劃。 – 2010-06-15 21:28:36

+0

好的。真的非常感謝你的評論!現在表現很合理。查看更新的問題。 – Karussell 2010-06-16 07:52:02

回答

2

在我的情況下,我知道該名稱是唯一的,但向name屬性添加索引註釋並沒有提高性能。瓶頸是findByName方法。

,直到你展示一些數字證明我錯了:)所以我不會相信這...:

  • 該指數產生仔細檢查(檢查DDL語句和數據庫)。您需要在此列上查詢此索引的索引。
  • 檢查生成的查詢的查詢計劃(應該是類似SELECT * FROM USER u WHERE u.NAME = 'foo')和執行時間。

之後,您可能會考慮激活二級緩存並緩存查詢。但數據庫是開始的地方(緩存太早會掩蓋真正的問題)。

並測量的東西! 如果你不能測量它,你不能改善它。 - 凱爾文勳爵

+0

當表格很小時,索引可能不被使用,儘管它看起來像完美匹配。 當然,這是完全可能的,OP添加了註釋,但沒有改變架構:) – 2010-06-15 20:16:09

+0

感謝您的建議!我現在將檢查索引是否已創建... – Karussell 2010-06-15 20:16:25

+0

@Jens Schauder:我應該如何更改模式? (我在慢速導入會話之前運行drop + create Schema) – Karussell 2010-06-15 20:17:36

3

您還沒有一個完整的答案提供足夠的信息,但這裏有一些想法:

  • 你可以使用id呢? Hibernate會準備查詢ID以供選擇,所以這些會比其他查詢更快(一點點)
  • 是否正確索引了名稱?爲了這個查詢的目的,它應該有一個唯一的鍵(你暗示着,你期望得到一個結果)。當然,這樣的索引在插入,更新和刪除方面性價比很高。
  • 當我們來參考時,它取決於你的表現是什麼意思:直到聲明返回的時間?那麼你應該使用延遲加載。它使第一個聲明更快,因此可能更快。當然,一旦你的參考文獻脫水了,你會有更多的陳述。否則(某些)急切的加載可能會更快,儘管這在很大程度上取決於細節。
  • 僱用緩存,這可能會特別有助於引用,如果可以從緩存中檢索。
  • 調整你的數據庫。給它足夠的記憶,以便將所有內容都保存在記憶中。
  • 調整您的網絡。對於所示的小查詢,延遲可能是一個問題
  • 通過將db放在與代碼相同的機器上來刪除網絡。假設它足夠大。

正如你所看到的,你有很多調整選項。我認爲唯一一個考慮索引的工作效果很好。

調整時:當然,當我們對這個問題的詳細信息(如表的完整結構,索引,Hibernate映射,表格的大小...)的基礎上的評論


UPDATE這可能會改變,第一個問題是:我們需要調整什麼? 是否將Criteria轉換爲SQL語句?如果這樣直接提供sql語句就可以完成這項工作。

它是SQL語句的實際執行嗎?如果是這樣,確定發佈代碼產生的sql語句將是第一件事。

我從來沒有見過存儲過程使事情變得更快的真實情況。當然,這並不意味着這種情況不存在。但現代rdbms的優化者非常聰明。

所以爲了正確啓動它:設置日誌記錄,以便您看到每個帶有精確時間戳的sql語句。以及您正在調整的整個過程的開始和結束時間。如果這是大約數百次的執行,你必須彙總一些東西。

這會告訴你它是否關於sql語句被執行以及哪個佔用很多時間,以及是否所有導致問題的sql語句。

大多數情況下,sql語句的性能很差,但不應該跳到結論。


更新的許多名稱部分:

您可以使用InExpression:http://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/criterion/InExpression.html找到多個對象一氣呵成。這將比單個查詢更快。

+0

對不起,沒有提供足夠的信息這是一個商業項目,我會盡量給你最大的信息,而不會讓我的老闆生氣。首先:我不能使用id。 我雖然我可以使用本機SQL查詢或存儲過程?這會提高性能嗎? – Karussell 2010-06-15 19:53:34