使用 HQL 进行休眠分页
Posted
技术标签:
【中文标题】使用 HQL 进行休眠分页【英文标题】:Hibernate Pagination using HQL 【发布时间】:2012-02-19 01:00:15 【问题描述】:休眠分页问题
我有一个与休眠分页有关的问题,在某种程度上这已在
中进行了解释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
这是查询的解释计划:
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外 | | 1 |简单 |获取请求0_ |参考 | TR_KIND_ID | TR_KIND_ID | 6 |常量 | 1703018 | 100.00 |使用位置 |
附加信息:在 50 条记录限制下使用和不使用 order by 子句的查询运行时间
如果我运行查询 with order
子句,则查询采用 0.0012s 并设置 LIMIT 50
和 without order
子句,相同的查询采用 0.0032s 与相同的LIMIT 50
。
如果:
-
特定的 HQL 查询正在访问数据库而不是缓存或从会话中获取信息?
HQL Query 是否总是会访问数据库以获取结果,而 Criteria 会访问会话或缓存并从中获取结果是真的吗?
也在我下面提到的查询中:
a) Query q = session.createQuery("from RequestDao r order by r.id desc");
b) q.setFirstResult(0);
c) q.setMaxResults(50);
在a处,我们是否真的从数据库中获取结果并将其存储在内存中或者如果没有,此时我们在结果集中有300万个结果,然后在b和c处我们设置偏移值和限制等等页面我们只会看到 50 个结果,所以现在剩下的 300 万条记录在哪里,在我们第二次调用这个查询时,我们是否再次去访问数据库并获取 300 万条记录并将它们放入内存中,然后再次在 c 处设置 50 条记录和继续下去。
这个问题对我来说不是很清楚,因此如果有人能提供清晰详细的解释,说明它是如何工作的,以及什么是这个问题的最佳解决方案,我将不胜感激。
更新
事实证明,我遇到的问题与页面上的记录显示无关,但我在该页面上有过滤器,并且在每次请求时,我都会再次从数据库中获取所有下拉值,并且发生了一些时髦的事情这导致页面加载时间增加。
我正在对数据库进行多个嵌套的休眠查询并返回结果,这个问题的最佳解决方案是什么?
【问题讨论】:
这个问题是开放的,因为我还没有得到我的问题的答案,如果你有任何信息,请添加答案。 我刚刚编辑了我的答案以反映更新。 【参考方案1】:您的查询告诉数据库对满足 WHERE 子句的所有记录进行排序。它可能会在返回前 50 名之前对数百万条记录进行排序。
编辑 1/26:现在具体问题已经澄清,我将尝试更具体地回答。
每次执行这样的查询时,Hibernate 都会进入数据库。更重要的是,它会将会话中的所有新/更新数据刷新到磁盘。如果这是您的情况,这种行为可能会导致速度变慢。
使用 Hibernate Query API 通常在大多数情况下表现良好,并且与各种数据库平台兼容。如果您真的担心从数据访问层中挤出最后一滴性能,您可以编写自己的本机 SQL 查询来选择前 50 个结果。但是一旦你这样做了,你几乎肯定会失去数据库的独立性。因此,请评估您的成本与收益。
您的查询运行时间似乎在毫秒范围内。这通常与将数据存储在磁盘上的关系数据库一样好。因此,您可能需要评估您是否确实存在性能问题。
编辑 1/27:好的。看起来像是页面整体设计的问题。我在过去 7 年左右一直在使用 AJAX,因此在浏览表格页面时,我通常不必等待过滤 UI 控件重绘。我想,在您的情况下,切换应用程序 UI 框架不是一种选择。您必须弄清楚如何优化下拉列表等的数据加载。这些数据是否经常变化?你能把它缓存在应用程序的某个地方吗?如果您必须每次都加载它们,您可以只获取显示字符串而不是整个对象吗?
【讨论】:
这可能是一个耗时且占用大量内存的过程? 还有什么其他方法可以优化查询以确保提高性能? 同样当我们说before returning you top 50
时,查询从数据库中获取结果后,它是否将这300万条记录保存在内存中并在内存中进行排序或数据到底在哪里?
@Rachel:排序是在数据库服务器上完成的。数据库服务器同时使用内存和硬盘空间。 REQUEST.ID 上的索引应该可以加快速度。
我们已经在 request.id 上有索引,所以我不知道这是否是问题。【参考方案2】:
如果您设置了第一个结果和最大结果数,Hibernate 将只从数据库中检索那些条目。如果这很慢,请将 max results 设置为 1 并启用 SQL 日志记录以查看还加载了哪些关联。
Hibernate 还支持多种缓存: * 一级缓存:只会在一个 Hibernate 会话期间使用,通常对应一个事务 * 二级缓存:在会话/事务边界上使用,但主要仅通过 id 方法查找 * 查询缓存:如果启用 Hibernate 将首先从那里查找查询结果。但是,查询在 HQL 和参数方面必须相同,因此每个页面可能会从数据库加载一次,然后被缓存。 (更多信息请看这里:http://docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance-querycache)
请注意,缓存需要堆内存,并且根据实体的大小缓存 300 万个实体可能会导致巨大的内存命中,从而增加垃圾收集,这将再次影响性能。
【讨论】:
嗯...那么解决这一挑战的合适方法是什么? 如果这是正确的,那么休眠只能从数据库中获取前 50 个结果,但我想知道为什么我的应用程序要花很多时间,我已经尝试记录 sql,这就是生成的休眠 sql,不知道是什么应该是解开这个谜团的下一步。 @Rachel 您是否尝试执行 Hibernate 生成的 SQL?需要很长时间吗?如果是这样,您是否有索引REQUEST.KIND
并且可能也在 REQUEST.ID
上?
我已经尝试过查询大约需要1 min
,但是当我从命令行运行该查询时,结果需要一些时间才能弹出。
@Thoms:我在KIND + ID
上有索引以上是关于使用 HQL 进行休眠分页的主要内容,如果未能解决你的问题,请参考以下文章