2012-08-28 86 views
14

我正在使用Hibernate 4.1.6,並且遇到了構建列表的速度問題。我正在運行以下查詢。爲什麼Hibernate的query.list()很慢?

public void doQuery(final Baz baz){ 
    final Query query = getSessionFactory().getCurrentSession().createQuery(
      "select c.id, foo.someValue from Foo as foo "+ 
      "join foo.a as a"+ 
      "join foo.b as b "+ 
      "join b.c as c "+ 
      "where baz=:baz" 
     ); 
    query.setParameter("baz", baz); 
    Long start=System.currentTimeMillis(); 
    final List<Object[]> list = query.list(); 
    Long end=System.currentTimeMillis(); 
    System.out.println((end-start)); 
} 

我設置了hibernate調試以獲取發送到數據庫的實際查詢。我直接在數據庫中運行該查詢,並在0.015 ms內返回了23,000行。所以,我猜猜查詢不是問題。上面的例子顯示它需要大約32秒來創建該列表。有什麼可以加快速度的嗎?

更新:我嘗試使用使用hibernate調試查詢的createSQLQuery()方法,它的運行速度與createQuery()方法一樣慢。

更新:我嘗試使用無狀態會話,但它運行速度一樣慢。

更新:我輸出一些統計數據(設置hibernate.generate_statistics標誌設置爲true),但看起來沒有什麼驚人的對我說:

Hibernate SessionFactory Statistics [ 
    Number of connection requests[4] 
    Number of flushes done on the session (either by client code or by hibernate[3] 
    The number of completed transactions (failed and successful).[3] 
    The number of transactions completed without failure[3] 
    The number of sessions your code has opened.[4] 
    The number of sessions your code has closed.[3] 
    Total number of queries executed.[4] 
    Time of the slowest query executed.[28258] 
    the number of collections fetched from the DB.[6] 
    The number of collections loaded from the DB.[6] 
    The number of collections that were rebuilt[0] 
    The number of collections that were 'deleted' batch.[0] 
    The number of collections that were updated batch.[0] 
    The number of your objects deleted.[0] 
    The number of your objects fetched.[1] 
    The number of your objects actually loaded (fully populated).[204] 
    The number of your objects inserted.[1] 
    The number of your object updated.[0] 
] 

Hibernate SessionFactory Query Statistics [ 
    total hits on cache by this query[0] 
    total misses on cache by this query[0] 
    total number of objects put into cache by this query execution[0] 
    Number of times this query has been invoked[1] 
    average time for invoking this query.[28258] 
    maximum time incurred by query execution[28258] 
    minimum time incurred by query execution[28258] 
    Number of rows returned over all invocations of this query[23303] 
] 

更新:我看到同樣的緩慢做從的ScrollableResults下一個()的時候從本地查詢。請注意,我在循環中沒有做任何事情。

ScrollableResults results = query.scroll(); 
    Long start=System.currentTimeMillis(); 
    while (results.next()) { 
     //do nothing 
    } 
    Long end=System.currentTimeMillis(); 
    System.out.println((end-start)); 
+0

可能是未提交交易阻止了作品。 –

+1

你可能想打印出你的hibernate sql。最有可能有一個n + 1選擇問題。 –

+0

約旦,我不知道任何交易阻止這個查詢。約翰,我沒有打印出上面提到的sql,查詢很好。 – user973479

回答

7

由於調整/優化問題總是很難查明,所以我不是100%確定的答案。

但是,根據您打開show_sql的事實,提取​​查詢並直接對數據庫運行,並通過Hibernate查詢看到亞秒級結果與執行時間,我將重點放在Hibernate的方式構建和保溼由query.list()調用產生的物體。

這裏是另一個用戶誰在Hibernate中提到的類似查詢的性能問題,並通過在POJO加滿方便的構造函數(構造函數接受一個值的每個字段)看到顯着的性能提升:Simple hibernate query returning very slowly

這聽起來像他們偶然發現了這個問題,並且沒有清楚地理解這個原因。有人猜測Hibernate必須使用反射來檢測屬性。我很好奇自己,並計劃深入研究Hibernate的源代碼,以便在有機會時更好地理解這一點。同時,你可能希望考慮添加這些完整的構造函數,併爲它們添加所有POJO類屬性的參數,並查看是否有所作爲。

請讓我知道你在找什麼,因爲我對Hibernate性能優化非常感興趣。謝謝!

+0

嗨,菲利普,感謝您的回覆!我確實看了一下這個鏈接,我所有的POJO都有完整的構造函數。不幸的是,這個解決方案不適用於我的情況。 – user973479

+0

嗯,太糟糕了,我希望這會有所幫助。看起來您在解決問題時非常全面,我真的很想幫忙。你可以發佈你的POJO嗎? –

+0

不幸的是:( – user973479

4

如果查詢(與show_sql)似乎沒有問題,那麼也許它在代碼中。啓動VisualVM(與JDK一起作爲jvisualvm),並使用其CPU分析器查找哪些方法花費的時間最長。

+0

對query.list的這種特殊調用是緩慢的罪魁禍首。希望有人能提供洞察力,爲什麼會出現這種行爲,可能是一種解決方法 – user973479

+1

我的意思是* query.list()調用哪個方法是罪魁禍首?挖掘Hibernate代碼以找到根本原因 –

+0

As前面提到過,沒有。 – user973479

1

我直接在數據庫中運行該查詢,並在0.015毫秒內返回23,000行。所以,我猜猜查詢不是問題。

這可能是不成熟的,因爲查詢執行時間大大依賴於查詢文本。即使他們的查詢是在相同的數據上運行的,您如何知道數據庫使用了相同的執行計劃?你怎麼知道它的磁盤緩存中獲得了相同數量的緩存命中?例如,hibernate在與數據庫交談時使用準備好的語句,但你可能沒有。在Oracle中,執行計劃被查詢文本緩存,所以不同的查詢文本意味着新計算的執行計劃。由於緩存的執行計劃可能是基於不同的查詢參數而形成的,因此它可能會有所不同 - 並且可以將執行時間改變幾個數量級。請注意,我並不是說它的數據庫,但我不會打折的可能性。

因此,您應該做的第一件事情就是測量數據庫或者在JVM中運行的東西是否正在浪費所有時間。一個簡單的方法是在查詢執行時觀察JVM的CPU消耗。如果它遠遠小於一個線程,那麼JVM正在等待一些東西 - 大概是數據庫。

如果是數據庫,請使用數據庫的優化工具來捕獲執行計劃和其他相關性能指標。

如果它位於JVM中,請使用Profiler來查明性能瓶頸。

0

我們遇到了類似的問題,不知道是否相關。像基本上,因爲我們每一次查詢newing了新SessionFactorys,它是做查詢:

select streamref0_.UUID as UUID145_, streamref0_.Tape_TapeId as Tape2_145_ from StreamRefToTape streamref0_ where streamref0_.UUID=? 

你會在那裏發現的大量涌現。事實證明,它每增加一個會話工廠就會增加一次。無論如何,這導致甲骨文花費所有的時間爲每個查詢做一個新的計劃(它報告cpu幾乎都在「硬解析」時間產生新的計劃 - 我想甲骨文是慢生成計劃,它沒有看到之前?)。在這種情況下的解決方法是每次只使用相同的工廠而不是新工廠。另見Hibernate produce different SQL for every query

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2588723819082解釋了難解析,這顯然是不好的。

另一種可能的解決辦法是使用「原始SQL」(JDBC)或休眠中可能原始的SQL查詢,但似乎沒有解決在某種程度上這種特殊情況下的問題...