使用HQL进行Hibernate分页

时间:2012-01-25 20:11:11

标签: java performance hibernate pagination hql

Hibernate分页问题

我有一个与Hibernate分页有关的问题,在某种程度上已经在

中解释了

Mysql Pagination Optimization

Using Hibernate's ScrollableResults to slowly read 90 million records

Hibernate - HQL pagination

Issues with Pagination and Sorting

Hibernate Row Pagination

详细

来自应用程序的HQL查询:

Query q = session.createQuery("from RequestDao r order by r.id desc");
            q.setFirstResult(0);
            q.setMaxResults(50);

查询返回300万条记录,对于分页,我们只设置了50条记录,分页页面非常慢,因为在每次刷新时我们都会调用查询获得300万条记录,而我们只设置50条记录。

我的主要问题是

  

HQL是否总是进入并命中数据库,或者它是否会进入会话或内存以查找数据,如果它每次都进入数据库并获得结果集那么从性能的角度来看它是非常合适的,最好的是什么改善它的解决方案?

在hibernate中使用HQL有一种方法可以查询数据库并首先获取50条记录,然后根据用户的要求获取其他记录。这个挑战真的让应用程序陷入困境,那么解决这个问题的最佳方法是什么?

在日志中生成的HQL查询

from com.delta.dao.RequestDao r order by r.id desc

Hibernate生成的查询

select
    getrequest0_.ID as ID24_,
    getrequest0_.TIME as START3_24_,
    getrequest0_.STAT as STATUS24_,
    getrequest0_.SUM as SUMMARY24_,
    getrequest0_.OUTNAME as OUTPUT7_24_,
    getrequest0_.INPNAME as INPUT8_24_,
    getrequest0_.REQUEST_DATE as requestT9_24_,
    getrequest0_.PARENT_ID as PARENT10_24_,
    getrequest0_.INTER_TYPE as INTERPO60_24_,
    getrequest0_.OPEN_INT as OPEN61_24_,
    getrequest0_.SOURCE_TYPE as SOURCE62_24_,
    getrequest0_.TARGET_TYPE as TARGET20_24_,
    getrequest0_.SOURCE as SOURCE14_24_,
    getrequest0_.COPY_DATA as COPY16_24_,
    getrequest0_.CURVE as GENERATE63_24_,
    getrequest0_.TITLE as TITLE24_,
    getrequest0_.TIME_ID as TIMESERIES12_24_,
    getrequest0_.TASK_NAME as TASK51_24_ 
from
    REQUEST getrequest0_ 
where
    getrequest0_.KIND='csv' 
order by
    getrequest0_.ID desc

以下是查询的解释计划


 | id | select_type | table        | type | possible_keys  | key        | key_len | ref          |  rows    | filtered | Extra       | 
 |  1 | SIMPLE      | getrequest0_ | ref  | TR_KIND_ID     | TR_KIND_ID | 6       | const        | 1703018  |   100.00 | Using where |

其他信息:查询50个记录限制的有和没有order by子句的运行时间


如果我运行查询with order子句,则查询将 0.0012s 设置为LIMIT 50without order子句,同样的查询需要相同LIMIT 50 0.0032s


我们如何才能找到:

  1. 特定的HQL查询是否正在访问数据库,而不是缓存或从会话中获取信息?
  2. HQL查询是否总是会进入并命中数据库以获得结果,并且Criteria会进入并点击会话或缓存并从中获取结果?
  3. 同样在我下面提到的查询中:

    a) Query q = session.createQuery("from RequestDao r order by r.id desc");
    b) q.setFirstResult(0);
    c) q.setMaxResults(50);
    
  4. 在a,我们从数据库获得结果并将其存储在内存中,或者如果没有,并且此时我们在结果集中有300万个结果,然后在b和c我们设置偏移值并限制等等,这是真的吗?页面我们只会看到50个结果所以现在剩下3百万条记录,在我们第二次调用这个查询时,我们再次进入数据库并获取300万条记录并将它们放入内存然后在c再次设置50条记录继续。

    这个问题对我来说并不清楚,因此如果有人能够提供明确而详细的解释,说明这是如何工作的,以及最适合解决这个问题的方法,我们将非常感激。

    更新

    事实证明,问题与页面上的记录显示无关,但我在该页面上有过滤器,并且每次请求都会从数据库中再次获取所有下拉值,并且有一些时髦的事情发生在那会导致页面加载时间增加。

    我正在对数据库进行多次嵌套的hibernate查询并获得结果,这个问题的最佳解决方案是什么?

2 个答案:

答案 0 :(得分:5)

您的查询告诉数据库对满足WHERE子句的所有记录进行排序。在返回前50名之前,它可能会对数百万条记录进行排序。

编辑1/26:现在澄清了具体问题,我将尝试更具体地回答。

  1. 每次执行这样的查询时,Hibernate都会进入数据库。更重要的是,它会将会话中的所有新/更新数据刷新到磁盘。如果这是您的情况,此行为可能会导致缓慢。

  2. 使用Hibernate查询API通常在大多数情况下都能很好地运行,并且与各种各样的数据库平台兼容。如果您真的担心从数据访问层中挤出最后一滴性能,您可以编写自己的本机SQL查询来选择前50个结果。但是一旦你这样做,你几乎肯定会失去数据库独立性。因此,请评估您的成本与收益。

  3. 您的查询运行时间似乎在单毫秒范围内。这通常与在磁盘上存储数据的关系数据库一样好。所以你可能想评估一下你是否确实遇到了性能问题。

    编辑1/27:好的。它看起来像页面整体设计中的问题。我在过去7年左右一直在使用AJAX,所以我通常不必等待过滤UI控件在浏览表格时重绘。我想,在您的情况下,切换应用程序UI框架不是一个选项。您必须弄清楚如何优化下拉列表等数据的加载。这些数据经常变化吗?你可以在应用程序的某个地方缓存它吗?如果你每次都必须加载它们,你能不能只获得显示字符串而不是整个对象?

答案 1 :(得分:1)

如果设置第一个结果和最大结果数,Hibernate将只从数据库中检索这些条目。如果这很慢,请将max results设置为1并启用SQL日志记录以查看还加载了哪些关联。

Hibernate还支持几个缓存: *第一级缓存:仅在一个Hibernate会话期间使用,该会话通常对应于一个事务 *二级缓存:用于会话/事务边界,但主要仅通过id方法查找 *查询缓存:如果启用,Hibernate将首先从那里查找查询结果。但是,查询在HQL和参数方面必须相同,因此每个页面可能会从DB加载一次然后被缓存。 (有关详细信息,请查看此处:http://docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance-querycache

请注意,缓存需要堆内存,并且根据实体的大小,缓存300万个实体可能会导致巨大的内存损失,从而增加垃圾收集,这会再次影响性能。